package com.alexanderlogistics; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Map; import java.util.Properties; import javax.mail.Folder; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Store; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicHeader; import org.junit.Test; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.mail.imap.IMAPFolder; import junit.framework.Assert; /** * This test shows how to access outlook via oauth2 * * See: https://www.java-forum.org/thema/javamail-mit-oauth2-an-o365.199133/ * * @author rsoika * */ public class TestIMAPOutlookOAuth { private static final String TENANT_ID = "51e2f038-0a96-41be-801d-bb4118aa018e"; private static final String CLIENT_ID = "39e9eec5-6939-47dc-9729-3438a9898e63"; private static final String CLIENT_SECRET = "A6x8Q~EWVUyt~gfyTHtIJf4ig.F9tajUpEEXMaCL"; /* * Neue Daten * * Wert: zDL8Q~iEy8AX~dEmIOHrVv3aBTCZLrHIFmShibZ_ * Geheime ID: bcaa558a-3300-4498-94e1-2267f418afd5 */ @Test public void TestMailboxImixs() { Store store = null; String token = null; try { // try to connect... token = getAuthToken(); Assert.assertNotNull(token); store = connect(token, "imixs@alexander-logistics.com"); Assert.assertNotNull(store); if (!store.isConnected()) { // not connected! } else { // read inbox.... IMAPFolder inbox = (IMAPFolder) store.getFolder("INBOX"); inbox.open(Folder.READ_WRITE); // fetches all messages from the INBOX... Message[] messages = inbox.getMessages(); System.out.println("..." + messages.length + " new messages found"); } } catch (IOException e) { e.printStackTrace(); Assert.fail(); } catch (MessagingException e) { e.printStackTrace(); Assert.fail(); } } @Test public void TestMailboxImixsBHV() { Store store = null; String token = null; try { // try to connect... token = getAuthToken(); Assert.assertNotNull(token); store = connect(token, "Imixsbhv@alexander-logistics.com"); // store = connect(token, "mgeisler@alexander-logistics.com"); Assert.assertNotNull(store); if (!store.isConnected()) { // not connected! } else { // read inbox.... IMAPFolder inbox = (IMAPFolder) store.getFolder("INBOX"); inbox.open(Folder.READ_WRITE); // fetches all messages from the INBOX... Message[] messages = inbox.getMessages(); System.out.println("..." + messages.length + " new messages found"); } } catch (IOException e) { e.printStackTrace(); Assert.fail(); } catch (MessagingException e) { e.printStackTrace(); Assert.fail(); } } /** * This method returns a connection to an javax.mail.store */ public Store connect(String oauth2AccessToken, String userEmailId) { try { String host = "outlook.office365.com"; String port = "993"; Store store = null; String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory"; Properties props = new Properties(); props.put("mail.imaps.ssl.enable", "true"); props.put("mail.imaps.sasl.enable", "true"); props.put("mail.imaps.port", port); props.put("mail.imaps.auth.mechanisms", "XOAUTH2"); props.put("mail.imaps.sasl.mechanisms", "XOAUTH2"); props.put("mail.imaps.auth.login.disable", "true"); props.put("mail.imaps.auth.plain.disable", "true"); props.setProperty("mail.imaps.socketFactory.class", SSL_FACTORY); props.setProperty("mail.imaps.socketFactory.fallback", "false"); props.setProperty("mail.imaps.socketFactory.port", port); props.setProperty("mail.imaps.starttls.enable", "true"); // optional set imap user? // props.put("mail.imap.user", userEmailId); props.put("mail.imaps.auth.plain.disable", "true"); props.put("mail.debug", "true"); props.put("mail.debug.auth", "true"); Session session = Session.getInstance(props); session.setDebug(true); store = session.getStore("imaps"); System.out.println("OAUTH2 IMAP trying to connect with system properties to Host:" + host + ", Port: " + port + ", userEmailId: " + userEmailId + ", AccessToken: " + oauth2AccessToken); store.connect(host, userEmailId, oauth2AccessToken); if (store.isConnected()) { System.out.println("Connection Established using imap protocol successfully !"); return store; } System.out.println("Store.Connect failed!"); } catch (Exception e) { System.out.println("Store.Connect failed with the errror: " + e.getMessage()); StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); String exceptionAsString = sw.toString(); System.out.println(exceptionAsString); Assert.fail(); } return null; } /** * Helper method to receive a Microsoft access Token * * https://jwt.ms/ * * "OAuth2": { * * "Authority": * https://login.microsoftonline.com/51e2f038-0a96-41be-801d-bb4118aa018e/v2.0, * * "ClientId": "39e9eec5-6939-47dc-9729-3438a9898e63", * * "ClientSecret": "A6x8Q~EWVUyt~gfyTHtIJf4ig.F9tajUpEEXMaCL" * * } * * * @return * @throws IOException * @throws ClientProtocolException */ public String getAuthToken() throws ClientProtocolException, IOException { CloseableHttpClient client = HttpClients.createDefault(); HttpPost loginPost = new HttpPost("https://login.microsoftonline.com/" + TENANT_ID + "/oauth2/v2.0/token"); String scopes = "https://outlook.office365.com/.default"; // scopes="https://outlook.office365.com/IMAP.AccessAsUser.All"; // scopes="https://graph.microsoft.com/.default"; // scopes="openid profile offline_access https://outlook.office365.com/SMTP.Send // https://outlook.office365.com/POP.AccessAsUser.All // https://outlook.office365.com/IMAP.AccessAsUser.All"; String encodedBody = "client_id=" + CLIENT_ID + "&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 parsed = new ObjectMapper().readValue(response, type); String result = parsed.get("access_token"); System.out.println("....access token = " + result); return result; } /** * Helper method to read bytes from strem * * @param inputStream * @return * @throws IOException */ private static byte[] readAllBytes(InputStream inputStream) throws IOException { final int bufLen = 4 * 0x400; // 4KB byte[] buf = new byte[bufLen]; int readLen; IOException exception = null; try { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { while ((readLen = inputStream.read(buf, 0, bufLen)) != -1) outputStream.write(buf, 0, readLen); return outputStream.toByteArray(); } } catch (IOException e) { exception = e; throw e; } finally { if (exception == null) inputStream.close(); else try { inputStream.close(); } catch (IOException e) { exception.addSuppressed(e); } } } }