title: Office 365 XOAUTH2 for IMAP and SMTP Authentication fails
body: <p>Recently the <a href="https://developer.microsoft.com/en-us/graph/blogs/announcing-oauth-2-0-support-for-imap-smtp-client-protocols-in-exchange-online/" rel="noreferrer">support for OAuth 2.0 for IMAP and SMTP in the Exchange Online</a> has been announced.
  Following <a href="https://docs.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth" rel="noreferrer">the guide</a> I've set up the application permissions and IMAP and SMTP connection.
  The application is configured as <code>Accounts in any organizational directory (Any Azure AD directory - Multitenant)</code> and uses <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow" rel="noreferrer">authorization code flow</a>.</p>
  <p>URLs below are used for authorization:</p>
  <ul>
  <li><a href="https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize" rel="noreferrer">https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize</a></li>
  <li><a href="https://login.microsoftonline.com/organizations/oauth2/v2.0/token" rel="noreferrer">https://login.microsoftonline.com/organizations/oauth2/v2.0/token</a></li>
  </ul>
  <p>And the following Delegated Microsoft Graph scopes have been added:</p>
  <p><a href="https://i.stack.imgur.com/A4qZS.png" rel="noreferrer"><img src="https://i.stack.imgur.com/A4qZS.png" alt="enter image description here" /></a></p>
  <p>The scopes, requests from code:</p>
  <pre class="lang-java prettyprint-override"><code>final List&lt;String&gt; scopes = Arrays.asList(
      &quot;offline_access&quot;,
      &quot;email&quot;,
      &quot;openid&quot;,
      &quot;profile&quot;,
      &quot;User.Read&quot;,
      &quot;Mail.ReadWrite&quot;,
      &quot;https%3A%2F%2Foutlook.office365.com%2FIMAP.AccessAsUser.All&quot;,
      &quot;https%3A%2F%2Foutlook.office365.com%2FSMTP.Send&quot;
  );
  </code></pre>
  <p>I successfully receive the access and refresh tokens:</p>
  <pre class="lang-js prettyprint-override"><code>{
      &quot;token_type&quot;: &quot;Bearer&quot;,
      &quot;scope&quot;: &quot;email IMAP.AccessAsUser.All Mail.ReadWrite openid profile SMTP.Send User.Read&quot;,
      &quot;expires_in&quot;: 3599,
      &quot;ext_expires_in&quot;: 3599,
      &quot;access_token&quot;: &quot;edited&quot;,
      &quot;refresh_token&quot;: &quot;edited&quot;,
      &quot;id_token&quot;: &quot;edited&quot;
  }
  </code></pre>
  <p>Here's the code, used to connect to IMAP:</p>
  <pre class="lang-java prettyprint-override"><code>Properties props = new Properties();
  props.put(&quot;mail.imap.ssl.enable&quot;, &quot;true&quot;);
  props.put(&quot;mail.imaps.sasl.enable&quot;, &quot;true&quot;);
  props.put(&quot;mail.imaps.sasl.mechanisms&quot;, &quot;XOAUTH2&quot;);
  props.put(&quot;mail.imap.auth.login.disable&quot;, &quot;true&quot;);
  props.put(&quot;mail.imap.auth.plain.disable&quot;, &quot;true&quot;);
  props.put(&quot;mail.debug&quot;, &quot;true&quot;);
  props.put(&quot;mail.debug.auth&quot;, &quot;true&quot;);
  
  Session session = Session.getInstance(props);
  session.setDebug(true);
  
  String userEmail = &quot;user@domain.onmicrosoft.com&quot;;
  String accessToken = &quot;access_token_received_on_previous_step&quot;;
  
  final Store store = session.getStore(&quot;imaps&quot;);
  store.connect(&quot;outlook.office365.com&quot;, 993, userEmail, accessToken);
  </code></pre>
  <p>Which generates the following output:</p>
  <pre><code>DEBUG: JavaMail version 1.6.2
  DEBUG: successfully loaded resource: /META-INF/javamail.default.address.map
  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 SASL
  DEBUG IMAPS: SASL mechanisms allowed: XOAUTH2
  DEBUG IMAPS: closeFoldersOnStoreFailure
  DEBUG IMAPS: trying to connect to host &quot;outlook.office365.com&quot;, port 993, isSSL true
  * OK The Microsoft Exchange IMAP4 service is ready. [QQBNADc...]
  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=user@domain.onmicrosoft.com, password=&lt;non-null&gt;
  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@17046283
  DEBUG IMAPS: SASL callback 1: javax.security.auth.callback.PasswordCallback@5bd03f44
  A1 AUTHENTICATE XOAUTH2 dXNlcj1o...
  A1 NO AUTHENTICATE failed.
  
  Exception in thread &quot;main&quot; javax.mail.AuthenticationFailedException: AUTHENTICATE failed.
      at com.sun.mail.imap.IMAPStore.protocolConnect(IMAPStore.java:732)
      at javax.mail.Service.connect(Service.java:366)
  </code></pre>
  <p>And the following code is used for connecting to SMTP:</p>
  <pre class="lang-java prettyprint-override"><code>Properties props = new Properties();
  props.put(&quot;mail.smtp.auth&quot;, &quot;true&quot;);
  props.put(&quot;mail.transport.protocol&quot;, &quot;smtp&quot;);
  props.put(&quot;mail.smtp.starttls.enable&quot;, &quot;true&quot;);
  props.put(&quot;mail.smtp.auth.mechanisms&quot;, &quot;XOAUTH2&quot;);
  props.put(&quot;mail.smtp.auth.login.disable&quot;,&quot;true&quot;);
  props.put(&quot;mail.smtp.auth.plain.disable&quot;,&quot;true&quot;);
  props.put(&quot;mail.debug.auth&quot;, &quot;true&quot;);
  
  Session session = Session.getInstance(props);
  session.setDebug(true);
  
  String userEmail = &quot;user@domain.onmicrosoft.com&quot;;
  String accessToken = &quot;access_token_received_on_previous_step&quot;;
  
  Transport transport = session.getTransport(&quot;smtp&quot;);
  transport.connect(&quot;smtp.office365.com&quot;, 587, userEmail, accessToken);
  </code></pre>
  <p>Which provides the output below:</p>
  <pre><code>DEBUG: setDebug: JavaMail version 1.6.2
  DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
  DEBUG SMTP: useEhlo true, useAuth true
  DEBUG SMTP: trying to connect to host &quot;smtp.office365.com&quot;, port 587, isSSL false
  220 AM5PR0701CA0005.outlook.office365.com Microsoft ESMTP MAIL Service ready at Mon, 4 May 2020 15:52:28 +0000
  DEBUG SMTP: connected to host &quot;smtp.office365.com&quot;, port: 587
  EHLO ubuntu-B450-AORUS-M
  250-AM5PR0701CA0005.outlook.office365.com Hello [my ip here]
  250-SIZE 157286400
  250-PIPELINING
  250-DSN
  250-ENHANCEDSTATUSCODES
  250-STARTTLS
  250-8BITMIME
  250-BINARYMIME
  250-CHUNKING
  250 SMTPUTF8
  DEBUG SMTP: Found extension &quot;SIZE&quot;, arg &quot;157286400&quot;
  DEBUG SMTP: Found extension &quot;PIPELINING&quot;, arg &quot;&quot;
  DEBUG SMTP: Found extension &quot;DSN&quot;, arg &quot;&quot;
  DEBUG SMTP: Found extension &quot;ENHANCEDSTATUSCODES&quot;, arg &quot;&quot;
  DEBUG SMTP: Found extension &quot;STARTTLS&quot;, arg &quot;&quot;
  DEBUG SMTP: Found extension &quot;8BITMIME&quot;, arg &quot;&quot;
  DEBUG SMTP: Found extension &quot;BINARYMIME&quot;, arg &quot;&quot;
  DEBUG SMTP: Found extension &quot;CHUNKING&quot;, arg &quot;&quot;
  DEBUG SMTP: Found extension &quot;SMTPUTF8&quot;, arg &quot;&quot;
  STARTTLS
  220 2.0.0 SMTP server ready
  EHLO ubuntu-B450-AORUS-M
  250-AM5PR0701CA0005.outlook.office365.com Hello [my ip here]
  250-SIZE 157286400
  250-PIPELINING
  250-DSN
  250-ENHANCEDSTATUSCODES
  250-AUTH LOGIN XOAUTH2
  250-8BITMIME
  250-BINARYMIME
  250-CHUNKING
  250 SMTPUTF8
  DEBUG SMTP: Found extension &quot;SIZE&quot;, arg &quot;157286400&quot;
  DEBUG SMTP: Found extension &quot;PIPELINING&quot;, arg &quot;&quot;
  DEBUG SMTP: Found extension &quot;DSN&quot;, arg &quot;&quot;
  DEBUG SMTP: Found extension &quot;ENHANCEDSTATUSCODES&quot;, arg &quot;&quot;
  DEBUG SMTP: Found extension &quot;AUTH&quot;, arg &quot;LOGIN XOAUTH2&quot;
  DEBUG SMTP: Found extension &quot;8BITMIME&quot;, arg &quot;&quot;
  DEBUG SMTP: Found extension &quot;BINARYMIME&quot;, arg &quot;&quot;
  DEBUG SMTP: Found extension &quot;CHUNKING&quot;, arg &quot;&quot;
  DEBUG SMTP: Found extension &quot;SMTPUTF8&quot;, arg &quot;&quot;
  DEBUG SMTP: protocolConnect login, host=smtp.office365.com, user=user@domain.onmicrosoft.com, password=&lt;non-null&gt;
  DEBUG SMTP: Attempt to authenticate using mechanisms: XOAUTH2
  DEBUG SMTP: Using mechanism XOAUTH2
  AUTH XOAUTH2 dXNlcj1obW9kaUB...
  535 5.7.3 Authentication unsuccessful [AM5PR0701CA0005.eurprd07.prod.outlook.com]
  
  Exception in thread &quot;main&quot; javax.mail.AuthenticationFailedException: 535 5.7.3 Authentication unsuccessful [AM5PR0701CA0005.eurprd07.prod.outlook.com]
      at com.sun.mail.smtp.SMTPTransport$Authenticator.authenticate(SMTPTransport.java:965)
      at com.sun.mail.smtp.SMTPTransport.authenticate(SMTPTransport.java:876)
      at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:780)
      at javax.mail.Service.connect(Service.java:366)
  </code></pre>
  <p>What I've also tried:</p>
  <ul>
  <li>specifying scopes as <code>https://graph.microsoft.com/SMTP.Send</code> and just <code>SMTP.Send</code></li>
  <li>using <code>https://login.microsoftonline.com/common/</code> url for authentication</li>
  </ul>
  <p>Result is always the same.</p>
  <p>Is it something I do wrong or there's a bug somewhere in the support for this from the Microsoft side?</p>
  <p><strong>Update 1:</strong></p>
  <p>Tried from the command line, but same result:</p>
  <pre><code>$ openssl s_client -crlf -connect outlook.office365.com:993
  ... connection part omitted
  * OK The Microsoft Exchange IMAP4 service is ready. [QQBNADYAUAAxADkAMgBDAEEAMAAwADkAMQAuAEUAVQBSAFAAMQA5ADIALgBQAFIATwBEAC4ATwBVAFQATABPAE8ASwAuAEMATwBNAA==]
  C01 CAPABILITY
  * CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=XOAUTH2 SASL-IR UIDPLUS ID UNSELECT CHILDREN IDLE NAMESPACE LITERAL+
  C01 OK CAPABILITY completed.
  A01 AUTHENTICATE XOAUTH2 dXNlcj1obW9kaUBjb...
  A01 NO AUTHENTICATE failed.
  * BYE Connection is closed. 13
  read:errno=0
  
  </code></pre>
  <p><strong>Update 2:</strong></p>
  <p>Tried to create brand new application in the Azure Portal with the following permissions:</p>
  <p><a href="https://i.stack.imgur.com/3EgWK.png" rel="noreferrer"><img src="https://i.stack.imgur.com/3EgWK.png" alt="enter image description here" /></a></p>
  <p>And receiving the following screen, when trying to give consent for scopes:</p>
  <p><a href="https://i.stack.imgur.com/mq001.png" rel="noreferrer"><img src="https://i.stack.imgur.com/mq001.png" alt="enter image description here" /></a></p>
  <p>That is odd, because the permissions from Azure Portal don't specify that the Admin consent is required and my previous app registration doesn't show such screen when IMAP and SMTP scopes are requested.</p>
  <p><strong>Update 3:</strong></p>
  <p>Thanks to comments to this post I tried the following scopes:</p>
  <pre class="lang-java prettyprint-override"><code>public static final List&lt;String&gt; SCOPES = Arrays.asList(
      &quot;offline_access&quot;,
      &quot;https%3A%2F%2Foutlook.office365.com%2FIMAP.AccessAsUser.All&quot;,
      &quot;https%3A%2F%2Foutlook.office365.com%2FSMTP.Send&quot;
  );
  </code></pre>
  <p>Which gave me the token below:</p>
  <pre class="lang-js prettyprint-override"><code>{
      &quot;token_type&quot;: &quot;Bearer&quot;,
      &quot;scope&quot;: &quot;https://outlook.office365.com/IMAP.AccessAsUser.All https://outlook.office365.com/SMTP.Send&quot;,
      &quot;expires_in&quot;: 3599,
      &quot;ext_expires_in&quot;: 3599,
      &quot;access_token&quot;: &quot;eyJ0eXAiOiJKV1....&quot;,
      &quot;refresh_token&quot;: &quot;OAQABAAAAAAAm....&quot;
  }
  </code></pre>
  <p><strong>IMAP/SMTP auth was successful</strong> and I was able to read the inbox + send an email!</p>
  <p>But for my application I need also couple of other scopes to use some MS Graph API endpoints (read user profile, messages subscription and messages deletion).</p>
  <p>So I tried different scopes:</p>
  <pre class="lang-java prettyprint-override"><code>public static final List&lt;String&gt; SCOPES = Arrays.asList(
          &quot;offline_access&quot;,
          &quot;User.Read&quot;,
          &quot;Mail.ReadWrite&quot;,
          &quot;https%3A%2F%2Foutlook.office365.com%2FIMAP.AccessAsUser.All&quot;,
          &quot;https%3A%2F%2Foutlook.office365.com%2FSMTP.Send&quot;
  );
  </code></pre>
  <p>This gave the token (note that scope value differs from the token that actually worked, the permissions don't have outlook url):</p>
  <pre class="lang-js prettyprint-override"><code>{
      &quot;token_type&quot;: &quot;Bearer&quot;,
      &quot;scope&quot;: &quot;IMAP.AccessAsUser.All Mail.ReadWrite SMTP.Send User.Read profile openid email&quot;,
      &quot;expires_in&quot;: 3599,
      &quot;ext_expires_in&quot;: 3599,
      &quot;access_token&quot;: &quot;eyJ0eXAiOiJKV1Q...&quot;,
      &quot;refresh_token&quot;: &quot;OAQABAAAAAAAm...&quot;
  }
  </code></pre>
  <p>Which led to the result I got previously:</p>
  <pre><code>A1 NO AUTHENTICATE failed.
  </code></pre>
  <p>Trying all the scopes to be as URLs:</p>
  <pre class="lang-java prettyprint-override"><code>public static final List&lt;String&gt; SCOPES = Arrays.asList(
          &quot;offline_access&quot;, // or &quot;https%3A%2F%2Fgraph.microsoft.com%2Foffline_access&quot;
          &quot;https%3A%2F%2Fgraph.microsoft.com%2FUser.Read&quot;,
          &quot;https%3A%2F%2Fgraph.microsoft.com%2FMail.ReadWrite&quot;,
          &quot;https%3A%2F%2Foutlook.office365.com%2FIMAP.AccessAsUser.All&quot;,
          &quot;https%3A%2F%2Foutlook.office365.com%2FSMTP.Send&quot;
  );
  </code></pre>
  <p>Leads to the following error when obtaining the token (the consent step passed successfully):</p>
  <pre class="lang-java prettyprint-override"><code>{
      &quot;error&quot;: &quot;invalid_request&quot;,
      &quot;error_description&quot;: &quot;AADSTS28000: Provided value for the input parameter scope is not valid because it contains more than one resource. Scope offline_access https://graph.microsoft.com/user.read https://graph.microsoft.com/mail.readwrite https://outlook.office365.com/imap.accessasuser.all https://outlook.office365.com/smtp.send is not valid.\r\nTrace ID: c3282396-6231-4e11-8300-77bc2ca57f00\r\nCorrelation ID: 5f5145bf-7114-4e6c-ab11-30e7ff84a056\r\nTimestamp: 2020-05-06 08:08:48Z&quot;,
      &quot;error_codes&quot;: [
          28000
      ],
      &quot;timestamp&quot;: &quot;2020-05-06 08:08:48Z&quot;,
      &quot;trace_id&quot;: &quot;c3282396-6231-4e11-8300-77bc2ca57f00&quot;,
      &quot;correlation_id&quot;: &quot;5f5145bf-7114-4e6c-ab11-30e7ff84a056&quot;
  }
  </code></pre>
  <p>And when trying all the scopes to have microsoft graph (as copied from the Azure Portal)</p>
  <pre class="lang-java prettyprint-override"><code>public static final List&lt;String&gt; SCOPES = Arrays.asList(
      &quot;https%3A%2F%2Fgraph.microsoft.com%2Foffline_access&quot;,
      &quot;https%3A%2F%2Fgraph.microsoft.com%2FUser.Read&quot;,
      &quot;https%3A%2F%2Fgraph.microsoft.com%2FMail.ReadWrite&quot;,
      &quot;https%3A%2F%2Fgraph.microsoft.com%2FIMAP.AccessAsUser.All&quot;,
      &quot;https%3A%2F%2Fgraph.microsoft.com%2FSMTP.Send&quot;
  );
  </code></pre>
  <p>Return the following token (without a refresh token althout offline_access has been requested)</p>
  <pre class="lang-js prettyprint-override"><code>{
      &quot;token_type&quot;: &quot;Bearer&quot;,
      &quot;scope&quot;: &quot;profile openid email https://graph.microsoft.com/IMAP.AccessAsUser.All https://graph.microsoft.com/Mail.ReadWrite https://graph.microsoft.com/SMTP.Send https://graph.microsoft.com/User.Read&quot;,
      &quot;expires_in&quot;: 3599,
      &quot;ext_expires_in&quot;: 3599,
      &quot;access_token&quot;: &quot;eyJ0eXAiOiJKV1...&quot;
  }
  </code></pre>
  <p>No success:</p>
  <pre><code>A1 NO AUTHENTICATE failed.
  </code></pre>
  <p>So it appears that if you don't specify Outlook url for scope it's assumed probably as Graph one which doesn't allow authorization through IMAP and SMTP.</p>
  <p><strong>Update 4:</strong></p>
  <p>By requesting all the scopes I need at consent step, then getting first access token with only Graph scopes and the second one using refresh token endpoint specifying Outlook scopes - it worked.
  Refresh token method for getting second access token is used because if you try to obtains access token by auth code you'll get get the following error:</p>
  <pre class="lang-js prettyprint-override"><code>{
      &quot;error&quot;: &quot;invalid_grant&quot;,
      &quot;error_description&quot;: &quot;AADSTS54005: OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token.\r\nTrace ID: 09fc80f4-f5fd-4e52-938f-d56b71dd0900\r\nCorrelation ID: 4f35e05c-23c8-4fdc-a5a7-2fcde5a73b44\r\nTimestamp: 2020-05-08 12:13:30Z&quot;,
      &quot;error_codes&quot;: [
          54005
      ],
      &quot;timestamp&quot;: &quot;2020-05-08 12:13:30Z&quot;,
      &quot;trace_id&quot;: &quot;09fc80f4-f5fd-4e52-938f-d56b71dd0900&quot;,
      &quot;correlation_id&quot;: &quot;4f35e05c-23c8-4fdc-a5a7-2fcde5a73b44&quot;
  }
  </code></pre>
  <p>So no I'll need to use two separate tokens depending on what resource I'll need to manage.</p>
  <p><strong>Update 5:</strong></p>
  <p>If it still doesn't work - check if your organization has Security Default enabled - they disable POP/IMAP/SMTP auth for accounts - <a href="https://techcommunity.microsoft.com/t5/exchange-team-blog/announcing-oauth-2-0-support-for-imap-and-smtp-auth-protocols-in/bc-p/1544725/highlight/true#M28589" rel="noreferrer">https://techcommunity.microsoft.com/t5/exchange-team-blog/announcing-oauth-2-0-support-for-imap-and-smtp-auth-protocols-in/bc-p/1544725/highlight/true#M28589</a></p>