How to Configure and Setup PHPMailer to Read Bounce Emails using oAuth2 From Outlook365

@arbialexandri Can you edit file admin/init.php and enable error reporting. On line 9

error_reporting(0);

to

error_reporting(-1);

The “blank” page might then have some extra information.

@duncanc, it seems like an SSL check issue:
<b>Fatal error</b>: Uncaught GuzzleHttp\Exception\RequestException: cURL error 60: SSL certificate problem: unable to get local issuer certificate (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration?appid=53ba6749-a598-4c05-b863-f8d75ca208e2 in C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\Handler\CurlFactory.php:211 Stack trace: #0 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\Handler\CurlFactory.php(158): GuzzleHttp\Handler\CurlFactory::createRejection(Object(GuzzleHttp\Handler\EasyHandle), Array) #1 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\Handler\CurlFactory.php(110): GuzzleHttp\Handler\CurlFactory::finishError(Object(GuzzleHttp\Handler\CurlHandler), Object(GuzzleHttp\Handler\EasyHandle), Object(GuzzleHttp\Handler\CurlFactory)) #2 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\Handler\CurlHandler.php(47): GuzzleHttp\Handler\CurlFactory::finish(Object(GuzzleHttp\Handler\CurlHandler), Object(GuzzleHttp\Handler\EasyHandle), Object(GuzzleHttp\Handler\CurlFactory)) #3 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\Handler\Proxy.php(28): GuzzleHttp\Handler\CurlHandler-&gt;__invoke(Object(GuzzleHttp\Psr7\Request), Array) #4 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\Handler\Proxy.php(48): GuzzleHttp\Handler\Proxy::GuzzleHttp\Handler\{closure}(Object(GuzzleHttp\Psr7\Request), Array) #5 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\PrepareBodyMiddleware.php(35): GuzzleHttp\Handler\Proxy::GuzzleHttp\Handler\{closure}(Object(GuzzleHttp\Psr7\Request), Array) #6 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\Middleware.php(31): GuzzleHttp\PrepareBodyMiddleware-&gt;__invoke(Object(GuzzleHttp\Psr7\Request), Array) #7 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\RedirectMiddleware.php(71): GuzzleHttp\Middleware::GuzzleHttp\{closure}(Object(GuzzleHttp\Psr7\Request), Array) #8 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\Middleware.php(63): GuzzleHttp\RedirectMiddleware-&gt;__invoke(Object(GuzzleHttp\Psr7\Request), Array) #9 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\HandlerStack.php(75): GuzzleHttp\Middleware::GuzzleHttp\{closure}(Object(GuzzleHttp\Psr7\Request), Array) #10 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\Client.php(331): GuzzleHttp\HandlerStack-&gt;__invoke(Object(GuzzleHttp\Psr7\Request), Array) #11 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\Client.php(107): GuzzleHttp\Client-&gt;transfer(Object(GuzzleHttp\Psr7\Request), Array) #12 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\Client.php(123): GuzzleHttp\Client-&gt;sendAsync(Object(GuzzleHttp\Psr7\Request), Array) #13 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\league\oauth2-client\src\Provider\AbstractProvider.php(608): GuzzleHttp\Client-&gt;send(Object(GuzzleHttp\Psr7\Request)) #14 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\league\oauth2-client\src\Provider\AbstractProvider.php(621): League\OAuth2\Client\Provider\AbstractProvider-&gt;getResponse(Object(GuzzleHttp\Psr7\Request)) #15 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\thenetworg\oauth2-azure\src\Provider\Azure.php(81): League\OAuth2\Client\Provider\AbstractProvider-&gt;getParsedResponse(Object(GuzzleHttp\Psr7\Request)) #16 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\thenetworg\oauth2-azure\src\Provider\Azure.php(93): TheNetworg\OAuth2\Client\Provider\Azure-&gt;getOpenIdConfiguration('common', '2.0') #17 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\league\oauth2-client\src\Provider\AbstractProvider.php(388): TheNetworg\OAuth2\Client\Provider\Azure-&gt;getBaseAuthorizationUrl() #18 C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\authorise.php(21): League\OAuth2\Client\Provider\AbstractProvider-&gt;getAuthorizationUrl() #19 C:\Base\www\phpList\3.6.10\lists\admin\index.php(771): include('C:\\Base\\www\\php...') #20 {main} thrown in <b>C:\Base\www\phpList\3.6.10\lists\admin\plugins\OAuth2\vendor\guzzlehttp\guzzle\src\Handler\CurlFactory.php</b> on line <b>211</b><br /> phpList version 3.6.10

@arbialexandri OK. I don’t know what that means, but I will modify the code to try to catch errors more cleanly.

@duncanc, I believe, where you initialize GuzzleHTTP package/class, you need to tell it to not check for SSL. Here is a reference I found that might point to some direction: GuzzleHttp Exception cURL error 60: SSL certificate problem: unable to get local issuer certificate · Issue #1935 · guzzle/guzzle · GitHub

I actually followed the instructions here GuzzleHttp Exception cURL error 60: SSL certificate problem: unable to get local issuer certificate · Issue #1935 · guzzle/guzzle · GitHub to fix it in my local by adding the certificate and restart PHP/Apache, but now:

  1. The app should be edited or registered as multi-tenant app. I changed mine from single to multi-tenant, and that part went away, but I am not sure why that is required or configured in the plugin. It might be better to make this a configurable thing.
  2. Now, after above changes, it seems like token is given, redirect happens, but PHPList doesn’t remember to capture/save thing since when I go back to the OAuth2 Access Topen view/page, I again get the login button and process bounces using oauth2 page shows that access token is needed.

I just wanted to share above, in case it might be of any use.

@arbialexandri What does “tenant” mean? Although there is a config setting for Tenant Id, it is not actually used because the example I looked at had no mention of it. I think that I have been using a default value of “common” which worked in my case. This is the package that the plugin now uses GitHub - TheNetworg/oauth2-azure: Azure AD provider for the OAuth 2.0 Client. . If your “tenant” value is different to “common” then I need to see how to set that.

I don’t know about the SSL certificate problem. I had no trouble with apache / php 8 on my local machine, so I don’t really understand the description of the problem in that link.

If you look in the phplist database at the config table, there should be a row with item value oauth2_access_token_json . If a token was successfully created then it will be stored there in json format.

There is a new release that should improve the error reporting.

@arbialexandri Just made a further change to use the tenant id. Up until now the code has been using “common”.

@duncanc, I just grabbed the latest version, and I also deployed/enabled the same on our actual public facing server which is Ubuntu 16.04 with PHP7.4 and did the same in the local environment, which is Windows with PHP8, and both still are not working.

Basically, clicking on the button, does the redirect and access token, I can see, is in the return/redirect URL, but PHPList doesn’t register it. There seems to be a flaw in the acceptance and writing the token/data.

If you remove the config settings rows from the database and re-do it on your end, I think you will be able to duplicate the issue. It seems like you have rows in the database that already make it functional for you, but if you were to start fresh, it doesn’t write them (at least that is what I am observing).

@arbialexandri I have just removed that row from the table, and created an access token successfully

Do you get redirected to the “token” page at the end of the process?
The Redirect URL should include a “code” parameter. That is used by the authorise page to request an access token.
If any of that fails, it should now display an error message.

@duncanc , I did remove the same row in my database and click on the button, upon redirect on the page, see below URL, I get all the data, but the row for “oauth2_access_token_json” was created with empty value! The code and everything is there and I do not see any error. Here is the page shown upon redirect:

/lists/admin/?pi=OAuth2&p=authorise&code=0.ASwAeJ9POu14n0mA670Lr_Yp0ElnulOYpQVMuGP411yiCOIsAAA.AgABAAIAAAD--DLA3VO7QrddgJg7WevrAgDs_wQA9P_IZoyRbI9YpDFeNWcJEr5UyOHqQCcWJXDsDoHLHkp_GzgjVYCyiNrZF8VQkOQFHwYzefQCRri3TugDHbCk6Vrlut1OP7z4a4ipbBqRNI1NeOScxn9nkxAB-2rfKV_qMTMcw8c6E16ma_NCNJBxNPFeMp8O1r47oCaqUA_tZhSBz8nHeq_z8Q3pgaCifKVrwtcHX5PLw00xBrxi9pczC6nDwzom8dnTRY6e0Pknn0CUzPLqIAGWDga0emlUKZkXh74R-CJ1dRwT1AVF4rckOGp3NDk1c24DSOwagELE86l9Ojvt6uwqkjVNZhv73v6NHRXq4uZykv0lnmuTekQhbnv1lcF2Od_Xinj-n5_DxzAv4EjK81GyndlN6oG_ZzkBMs52869ZjdqjSxhcqmihkRKcvIjms9es_HoiBZ73iV8Bdr_CYOOsUBeXAwaK3GEALrV3-fAJ8wvQM8Lk4XA-WL10iTxMb7KNaFwT4WPcsF1Udb3JezZM8G-3oT-kLM1cyRBeigPLTdlGlmn-i8oBQcmNaUOsSt-84zXZYGSfiep8ux5RZOwa4eBUF-yEDYGVXP5koUu8YnYFaO1pjWiEkPpNtmZF99zKoz9M-0MFiZ7quP8Ehd-V0hPTe95yotn0lcTCfJDgFpqFwiJniL_-bi5wTa-WF_VoGjUCTd2o6QWhAr73p4H4-NLV80GF8hQUn8FQwFrX5RRtMU4C8P4vyZdY_pNEBISTU7HFFBMGZ5ZYZmCDYMxT-yDeW1uT7ZqwqtrXG65qWAvRgFrfiiVcLZ_66Lkj5S0Pfv9d-KwqFm3yxedpEkuvIFSC9SO15Q&state=99c549ada7860c875e95fe2e6ca85a35&session_state=d1db55e5-51ed-48ac-8c48-b9db1f4792b0#
Here is my php modules installed, in case the operation you have implemented needs a specific module to be turned on!

[PHP Modules]
bcmath
calendar
Core
ctype
curl
date
dom
exif
fileinfo
filter
ftp
gd
hash
iconv
imap
intl
json
libxml
mbstring
mysqli
mysqlnd
openssl
pcre
PDO
Phar
readline
Reflection
session
SimpleXML
soap
SPL
standard
tokenizer
xml
xmlreader
xmlwriter
xsl
zip
zlib

@arbialexandri Ah, OK I now see what might be happening. The redirect doesn’t seem to be working so the processing to use the code to request an access token is not being run.

If you click in the URL bar and press enter, so that the page is requested then that should display the token page.

@duncanc, I tried that, but still nothing.

I must state that I again went back to release 1.1.3 and given my same environment without any changes, the plugin and flow becomes functional. I did this to assure I am not missing anything, just in case.

@arbialexandri Thanks. I noticed that you are using Chrome. I have been using Firefox and have just tried Chrome and the redirect goes to the same page that you showed. I’m not sure why phplist is doing that, possibly a session problem.

But when I requested the page in the URL bar I went to the token page showing the new token.

Let’s leave it for now. I will see whether I can improve the error reporting a bit more.

@duncanc, thank you sir for all the support in regard to this matter. I hope to hear back from you once a solution is in place.

Just to add, I tried the same in Firefox 106.0.5, Chrome and Edge chromium the latest versions and the same issue persists in all, against both the local and deployed/live environments.

@arbialexandri Thanks for that update. I have tried chrome again and now it works!
Are you able to give me access to one of your systems so that I can see the problem? Not now, but maybe tomorrow.

@duncanc, what type of access might you need? Are you referring to a remote access and/or screen sharing kind of a thing? I can share my local environment via Zoom, Teams, … if that would work.

@arbialexandri I was thinking of phplist login but you might not be allowed to do that. Screen sharing would be possible using zoom though.

Let me know tomorrow. I am in London so you might need to consider any time difference.

@duncanc, yeah, I can only share the local environment via zoom meeting. I am in Los Angeles, but I am sure we can find an overlapping time to contact and go over this issue.

I will leave a note here once I start working tomorrow and will monitor your reply whenever you become available, and we will get on a call to go over this issue.

@duncanc, I wanted to see if you will have some time to get on a quick call as you had suggested. I will be available in the next hour or two. Once you confirm, either you or I can share a zoom meeting link.

@duncanc, per our discussion/conversation, I am writing back to report my findings/progress:

  1. The overall plugin functionality is great and works as intended
  2. I came across a unique situation where we had built up a huge bounce email count, of more than 40K (large number of bounces), since we couldn’t process them for a while. Thanks to the plugin you have provided, now the bounces do get processed, but there seems to be an issue with large number of emails where while the processing is reading the emails, at some point the authorization token is expired, but PHPList doesn’t seem to refresh it to continue and read more and as a result the bounce processing stops and incorrectly reports that all the emails were processed/done.
  3. At this point, since the process is interrupted, and token is not checked to be valid, those emails that were read and processed and were supposed to be purged/deleted, do not get removed and stary in the inbox/folder; hence leaving us with a large number of emails that are read/processed to delete manually in order to clean up the inbox/folder.
  4. I suggest that the logic to read/process and then delete the emails at the end to change to read/process and delete per email, so the inbox/folder is cleaned up as PHPList plows forward to process them.

I hope above make sense and can be looked into to enhance/improve.

@arbialexandri Thanks. I think that each bounce email is processed then deleted from the mailbox one at a time, not waiting until the end.

I did wonder whether the token was validated only once when connecting or repeatedly.

I can change the code to refresh the token at the start of processing bounces regardless of having expired, so that it will be valid for as long as possible.

Update

There is a new version of the plugin that refreshes the token each time processing bounces.

Also, a setting to limit the number of bounce emails that are processed in one run. I suggest setting that to something relatively small, say the default of 1000, which should limit the risk of the process running for too long. So long as you run the process quite often, say several times an hour, then that should work through a large number of bounces without a problem.