title: SMTP to O365 using OAUTH2 and PHPMailer
body: <p>I’m converting an existing PHPMailer app with Basic (userid and password) Authentication to use OAUTH2. Setup was easy but the app fails on authentication.</p>
  
  <pre><code>2020-05-23 15:14:59 Connection: opening to smtp.office365.com:587, timeout=300, options=array()
  2020-05-23 15:14:59 Connection: opened
  2020-05-23 15:14:59 SMTP INBOUND: "220 LO2P265CA0358.outlook.office365.com Microsoft ESMTP MAIL Service ready at Sat, 23 May 2020 15:14:58 +0000"
  2020-05-23 15:14:59 SERVER -&gt; CLIENT: 220 LO2P265CA0358.outlook.office365.com Microsoft ESMTP MAIL Service ready at Sat, 23 May 2020 15:14:58 +0000
  2020-05-23 15:14:59 CLIENT -&gt; SERVER: EHLO [my domain name]
  2020-05-23 15:14:59 SMTP INBOUND: "250-LO2P265CA0358.outlook.office365.com Hello [91.208.99.2]"
  2020-05-23 15:14:59 SMTP INBOUND: "250-SIZE 157286400"
  2020-05-23 15:14:59 SMTP INBOUND: "250-PIPELINING"
  2020-05-23 15:14:59 SMTP INBOUND: "250-DSN"
  2020-05-23 15:14:59 SMTP INBOUND: "250-ENHANCEDSTATUSCODES"
  2020-05-23 15:14:59 SMTP INBOUND: "250-STARTTLS"
  2020-05-23 15:14:59 SMTP INBOUND: "250-8BITMIME"
  2020-05-23 15:14:59 SMTP INBOUND: "250-BINARYMIME"
  2020-05-23 15:14:59 SMTP INBOUND: "250-CHUNKING"
  2020-05-23 15:14:59 SMTP INBOUND: "250 SMTPUTF8"
  2020-05-23 15:14:59 SERVER -&gt; CLIENT: 250-LO2P265CA0358.outlook.office365.com Hello [91.208.99.2]250-SIZE 157286400250-PIPELINING250-DSN250-ENHANCEDSTATUSCODES250-STARTTLS250-8BITMIME250-BINARYMIME250-CHUNKING250 SMTPUTF8
  2020-05-23 15:14:59 CLIENT -&gt; SERVER: STARTTLS
  2020-05-23 15:14:59 SMTP INBOUND: "220 2.0.0 SMTP server ready"
  2020-05-23 15:14:59 SERVER -&gt; CLIENT: 220 2.0.0 SMTP server ready
  2020-05-23 15:14:59 CLIENT -&gt; SERVER: EHLO [my domain name]
  2020-05-23 15:14:59 SMTP INBOUND: "250-LO2P265CA0358.outlook.office365.com Hello [91.208.99.2]"
  2020-05-23 15:14:59 SMTP INBOUND: "250-SIZE 157286400"
  2020-05-23 15:14:59 SMTP INBOUND: "250-PIPELINING"
  2020-05-23 15:14:59 SMTP INBOUND: "250-DSN"
  2020-05-23 15:14:59 SMTP INBOUND: "250-ENHANCEDSTATUSCODES"
  2020-05-23 15:14:59 SMTP INBOUND: "250-AUTH LOGIN XOAUTH2"
  2020-05-23 15:14:59 SMTP INBOUND: "250-8BITMIME"
  2020-05-23 15:14:59 SMTP INBOUND: "250-BINARYMIME"
  2020-05-23 15:14:59 SMTP INBOUND: "250-CHUNKING"
  2020-05-23 15:14:59 SMTP INBOUND: "250 SMTPUTF8"
  2020-05-23 15:14:59 SERVER -&gt; CLIENT: 250-LO2P265CA0358.outlook.office365.com Hello [91.208.99.2]250-SIZE 157286400250-PIPELINING250-DSN250-ENHANCEDSTATUSCODES250-AUTH LOGIN XOAUTH2250-8BITMIME250-BINARYMIME250-CHUNKING250 SMTPUTF8
  2020-05-23 15:14:59 Auth method requested: XOAUTH2
  2020-05-23 15:14:59 Auth methods available on the server: LOGIN,XOAUTH2
  2020-05-23 15:14:59 CLIENT -&gt; SERVER: AUTH XOAUTH2 [long string of chars ending in == - base 64 encoded?] 
  2020-05-23 15:15:04 SMTP INBOUND: "535 5.7.3 Authentication unsuccessful [LO2P265CA0358.GBRP265.PROD.OUTLOOK.COM]"
  2020-05-23 15:15:04 SERVER -&gt; CLIENT: 535 5.7.3 Authentication unsuccessful [LO2P265CA0358.GBRP265.PROD.OUTLOOK.COM]
  2020-05-23 15:15:04 SMTP ERROR: AUTH command failed: 535 5.7.3 Authentication unsuccessful [LO2P265CA0358.GBRP265.PROD.OUTLOOK.COM]
  SMTP Error: Could not authenticate.
  2020-05-23 15:15:04 CLIENT -&gt; SERVER: QUIT
  </code></pre>
  
  <p>My composer.json is just:</p>
  
  <pre><code>"require": {
      "phpmailer/phpmailer": "~6.1",
      "stevenmaguire/oauth2-microsoft": "2.2.0"
  </code></pre>
  
  <p>which then also drags in the League oauth2-client and others.  </p>
  
  <p>To avoid the issue of OAUTH2 only working for free consumer accounts and not O365 (see Issue 3 - stevenmaguire/oauth2-microsoft on github - and I have replicated the issue to confirm it), I have edited /src/Provider/Microsoft.php as follows:</p>
  
  <pre><code>  protected $urlAuthorize = 'https://login.microsoftonline.com/common/oauth2/authorize';
    protected $urlAccessToken = 'https://login.microsoftonline.com/common/oauth2/token';
  </code></pre>
  
  <p>(This uses the Graph V1 endpoint. Note that the latest Graph endpoint V2 is accessed via ... oauth2/v2.0/token and ...oauth2/v2.0/authorize and that either of these uses different Scope parameters that are incompatible with the old 'wl' Windows Live ones) </p>
  
  <pre><code>My relevant PHPMailer code is:
  </code></pre>
  
  <pre class="lang-php prettyprint-override"><code>    $mail = new PHPMailer;
      $mail-&gt;isSMTP();                           // Set mailer to use SMTP
      $mail-&gt;Host = 'smtp.office365.com';        // Specify main and backup SMTP servers
      $mail-&gt;SMTPAuth = true;                    // Enable SMTP authentication
      $mail-&gt;AuthType = 'XOAUTH2';               // omit this to send using basic authentication
      $username = [my email address - as Azure AD signin];    
      $clientId = [from Azure AD - redacted];
      $clientSecret = ‘[from Azure AD - redacted];
      $refreshToken = [long char string – from running get_auth_token];
  
      $provider = new Microsoft (
          [
          'clientId' =&gt; $clientId ,
          'clientSecret' =&gt; $clientSecret
          ]
      );
  
  
      $mail-&gt;setOAuth(
          new OAuth(
              [
              'provider' =&gt; $provider,
              'clientId' =&gt; $clientId,
              'clientSecret' =&gt; $clientSecret,
              'refreshToken' =&gt; $refreshToken’,
              'userName' =&gt;$username
              ]
          )
      );
  
  
      //$mail-&gt;Username = blah;                  // SMTP username – not needed for OAUTH2
      //$mail-&gt;Password = blah;                  // SMTP password – not needed for OAUTH2
      $mail-&gt;SMTPSecure = 'tls';                 // Enable TLS encryption, `ssl` also accepted
      $mail-&gt;Port = 587;  
      $mail-&gt;SMTPDebug = SMTP::DEBUG_LOWLEVEL;
  </code></pre>
  
  <p>My get_oauth_token.php contains:</p>
  
  <pre><code>$clientId = [my client ID];
  $clientSecret = [my client secret]';
  $redirectUri = [the full URI of get_oauth_token.php];
  </code></pre>
  
  <p>I have created an app in MSFT Azure AD of type ‘Web app’, taken the client ID and client secret from there, and added API permissions for SMTP.Send, Mail.Send, offline_access, and openid</p>
  
  <p>If I can find where in the code the Azure AD authentication codes and messages (vide <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/reference-aadsts-error-codes#lookup-current-error-code-information" rel="nofollow noreferrer">https://docs.microsoft.com/en-us/azure/active-directory/develop/reference-aadsts-error-codes#lookup-current-error-code-information</a>) are returned, I'll be a step forward! I have tried the AD Monitor for sign-ins, but these particular unsuccessful attempts are not logged - nor the successful getting of the refresh token. </p>