Reputation: 1151
I have currently been trying to access a office 365 account via the IMAP protocol. I am using Jakarta Mail (Javax.mail) to try the connection. When I try to authenticate I get the following error:
DEBUG IMAP: trying to connect to host "imap-mail.outlook.com", port 993, isSSL true
* OK The Microsoft Exchange IMAP4 service is ready. [...]
A0 CAPABILITY
* CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=XOAUTH2 SASL-IR UIDPLUS ID UNSELECT CHILDREN IDLE NAMESPACE LITERAL+
A0 OK CAPABILITY completed.
DEBUG IMAP: AUTH: PLAIN
DEBUG IMAP: AUTH: XOAUTH2
DEBUG IMAP: protocolConnect login, host=imap-mail.outlook.com, user=[...], password=<non-null>
DEBUG IMAP: SASL Mechanisms:
DEBUG IMAP: XOAUTH2
DEBUG IMAP:
DEBUG IMAP: SASL client XOAUTH2
DEBUG IMAP: SASL callback length: 2
DEBUG IMAP: SASL callback 0: javax.security.auth.callback.NameCallback@11d86b9d
DEBUG IMAP: SASL callback 1: javax.security.auth.callback.PasswordCallback@6dce59e
A1 AUTHENTICATE XOAUTH2 [...]
A1 NO AUTHENTICATE failed.
With the system that I am making it is a requirement that I don't have to manually get the access token every time from a login link. To this extent, I thought the smartest move would be to use grant_type=client_credentials
. However, I have not been getting this to work.
The code I use for the connection is as follows:
Properties props = new Properties();
// SMTP
props.put("mail.smtp.host", "smtp-mail.outlook.com");
props.put("mail.smtp.port", "587");
props.put("mail.smtp.starttls.enable", "true");
// IMAP
props.put("mail.imap.ssl.enable", "true"); // required for Gmail
props.put("mail.imap.sasl.enable", "true");
props.put("mail.imap.sasl.mechanisms", "XOAUTH2");
props.put("mail.imap.auth.login.disable", "true");
props.put("mail.imap.auth.plain.disable", "true");
// Debug
props.put("mail.debug", "true");
props.put("mail.debug.auth", "true");
Session session = Session.getInstance(props);
session.setDebug(true);
String accessToken = renewToken();
try {
Store mailStore = session.getStore("imap");
mailStore.connect("imap-mail.outlook.com", System.getenv("MAIL_USERNAME"), accessToken);
} catch (MessagingException e) {
throw new RuntimeException(e);
}
and the renewToken()
looks like the following:
@Nullable
private String renewToken() {
if(System.currentTimeMillis() > tokenExpires) {
try {
String request = "client_id=" + URLEncoder.encode(System.getenv("CLIENT_ID"), StandardCharsets.UTF_8)
+ "&client_secret=" + URLEncoder.encode(System.getenv("CLIENT_SECRET"), StandardCharsets.UTF_8)
+ "&scope=" + URLEncoder.encode("https://outlook.office365.com/.default", StandardCharsets.UTF_8)
+ "&grant_type=client_credentials";
HttpURLConnection conn = (HttpURLConnection) new URL(TOKEN_URL).openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
PrintWriter out = new PrintWriter(conn.getOutputStream());
out.print(request);
out.flush();
out.close();
conn.connect();
HashMap<String,Object> result = new ObjectMapper().readValue(conn.getInputStream(), new TypeReference<>() {});
tokenExpires = System.currentTimeMillis()+(((Number)result.get("expires_in")).intValue()* 1000L);
return (String)result.get("access_token");
} catch (IOException e) {
LOGGER.error(e.getMessage());
}
}
return null;
}
Finally the CLIENT_AUTHORITY is defined as https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Where the {tenant}
is replaced with the tenant id from https://portal.azure.com
As it is now, I am not really sure why I am getting the error, and don't really understand the meaning of the "NO AUTHENTICATE failed" error. The only thing that comes to mind is that the access token is either wrongly generated or I am not requesting the correct permissions.
It should also be mentioned that I have tried with the host "outlook.office365.com" instead of "imap-mail.outlook.com" without any luck.
Upvotes: 1
Views: 8067
Reputation: 1151
I got it working, by following this guide:
Authentication Failure for IMAP using Client Credential flow for OAuth2.0 | Java | Exchange Online
The error for me was that the guide provided on the https://learn.microsoft.com website did not explain how to use the commands needed to enable OAuth2.0 properly.
Upvotes: 1