Google and Microsoft intend to stop support for password-based login to IMAP and POP, beginning on 2020-06-15 and 2020-10-13, respectively. The alternative, OAuth2-based login, will continue to work as the only means to authenticate users to these providers' mail servers.
Applications using the PHP IMAP extension for IMAP and POP access do not currently have a means to present OAuth tokens for authentication. This means that these applications will no longer be able to access Google and Microsoft email over IMAP and POP after these dates. We, the maintainers of the IMAP extension, do not wish to see our users lose access, so we are expediting the addition of token-based authentication.
Currently, to access a Google-hosted inbox using the PHP IMAP extension, you do something like this:
$mbh = imap_open('{imap.gmail.com:993/imap/ssl/}INBOX', $username, $password); if (! $mbh) { error_log(imap_last_error()); throw new \RuntimeException('Unable to open Google INBOX'); }
This is classic password-based authentication, which Google (rightly) considers less secure than a time-limited, token-based authentication. In fact, to even get the above code to work, you need to instruct Google to allow “Less Secure Apps”. A similar situation applies for Microsoft 365, just the terminology is a bit different.
When these providers stop support for passwords, the above code will no longer work, period. Instead, the application will first need to register with Google as an application, then update its code to acquire an access token, then pass the access token to the IMAP server instead of the password.
The new code is substantially more complicated. I'll give an outline here (refer to the Google OAuth 2 documentation for more detail including a diagram):
For more on OAuth, refer to this OAuth primer by Aaron Parecki.
If you are the author of a PHP application using imap_open to access Google or Microsoft IMAP or POP servers, then you need to:
In addition:
Don't forget:
As you can see, this is not just a drop-in replacement. Once your application can speak an OAuth flow, then you need to pass the token to imap_open. We have some work to do here:
The plan is to add token support to imap_open, as follows:
$mbh = imap_open($server, $username, $token, OP_XOAUTH2); if (! $mbh) { error_log(imap_last_error()); throw new \RuntimeException('Unable to open Google INBOX'); }
Here, the parameter described as “password” takes the token received from the OAuth flow, and the function's told it's a token by the newly added option OP_XOAUTH2. Internally, this will cause the function to use the XOAUTH2 mechanism during the AUTHENTICATE sequence, which is transparent to consumers.
OAuth 2 supports an extra challenge phase, whereby one has to supply multi-factor authentication beyond just the token. In this first implementation, because of the looming deadline, imap_open will not support this. However, a future implementation might, using an API like follows:
$options = []; if ($_GET['mfa']) { $options['OP_XOAUTH2_CHALLENGE'] = $_GET['mfa']; } $mbh = imap_open($server, $username, $token, OP_XOAUTH2, $max_retries, $options); if (! $mbh) { if (IMAP_XOAUTH2_CHALLENGE === imap_last_error_code()) { // show form collecting MFA into 'mfa' variable } else { error_log(imap_last_error()); throw new \RuntimeException('Unable to open Google INBOX'); } }
The basic idea is to first try an OAuth authentication. If that fails, prompt the user to enter their MFA, then on the next submission, pass the challenge value to the open.
This is a work in progress. Plan changes will be posted here. Status reports will be blogged. Feel free to ask a question.