MS Exchange - IMAP-XOAuth2 authentication fails
Overview
#2141 (closed) introduced support for (1) obtaining OAuth2 tokens and (2) using the OAuth2 tokens for IMAP-XOAuth2.
In my test setup, I am able to obtain a token from Microsoft which appears valid -- but their IMAP server is not accepting the token.
The "Critical Evaluation" below include a more thorough review of theories/data, but here's a high-level summary of debugging efforts:
Provider | OAuth2 initiation | Use token for IMAP/XOAuth2 | Use token for HTTP/REST |
---|---|---|---|
Microsoft Exchange Online | ![]() |
![]() |
![]() |
Google Mail | ![]() |
![]() |
(not tested) |
Reproduction steps
Configure Civi's OAuth client as described in the draft documentation: documentation/docs/sysadmin!278 (merged)
Recap:
- Enable
oauth-client
- Register the OAuth details in Civi and in Azure Portal.
- In Civi's "Admin => CiviMail => Mail Accounts", add a mail account
- For the newly created account, choose "Save & Test".
Current behavior
The IMAP server responds with NO AUTHENTICATE failed.
and * BAD Command Error. 12
In the GUI, it appears as:
On a network level, the interaction is:
<< * OK The Microsoft Exchange IMAP4 service is ready. [{_LONG_RANDOM_CODE_}]
>> A0001 AUTHENTICATE XOAUTH2 {_THE_XOAUTH2_TOKEN_}
<< A0001 NO AUTHENTICATE failed.
>>
<< * BAD Command Error. 12
>> A0002 LOGOUT
<< * BAD Command Error. 12
(To see this, I applied a hack to log network I/O and re-ran the check via CLI.)
Expected behavior
The IMAP test should succeed.
Environment information
- Browser: Firefox
- CiviCRM: 5.32.beta1
- PHP: 7.1
- CMS: Drupal 7
- Database: MySQL 5.6
- Web Server: Apache 2.4
Critical evaluation
Here are a few theories for where the problem may reside.
- The problem is in the OAuth2 client.
- The OAuth2 client is broken - it does not obtain a real token from Microsoft.
- The OAuth2 client obtains a real token - but the token does not have the proper scopes.
- The OAuth2 client obtains a valid token with proper scopes - but it doesn't speak IMAP-XOAuth2 correctly.
- The problem is in the OAuth2 service provider (either the authorization-server or the resource-server/IMAP-server).
- There is a problem in the account flags/configuration.
- There is a bug in the OAuth2 service provider.
Let's consider each in turn to see if we can prove/disprove the theory:
(Theory 1.1) The OAuth2 client is broken - it does not obtain a real token from Microsoft.
This explanation doesn't hold -- because the same token works for other scenarios.
-
Navigate to "Admin => System Settings => OAuth => ms-exchange"
-
Click "Inspect"
-
Find "Access Token: Raw". Copy the value.
-
In CLI, make an HTTP request to MS with the same token:
$ curl 'https://graph.microsoft.com/v1.0/me' \ -H 'Accept: application/json' \ -H 'Authorization: Bearer <TOKEN_VALUE>' {"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#users/$entity","businessPhones":["XXX-XXX-XXXX"],"displayName":"My Name","givenName":"My","jobTitle":null,"mail":"myname@wariomail.onmicrosoft.com","mobilePhone":null,"officeLocation":null,"preferredLanguage":"en-US","surname":"Name","userPrincipalName":"myname@wariomail.onmicrosoft.com","id":"XXXXX-XXXX-XXXXX"}
-
The response is well-formed and has the correct information.
(Theory 1.2) The OAuth2 client obtains a real token - but the token does not have the proper scopes.
I can show that the client works according to spec.
-
In the Microsoft documentation Authenticate an IMAP, POP or SMTP connection using OAuth, we can see the required scope is
IMAP.AccessAsUser.All
. -
In Azure Portal, navigate to "Azure Active Directory => App registrations => (my-app) => API permissions". Note the list of permissions includes IMAP:
-
In CiviCRM, navigate to "Admin => System Settings => OAuth => ms-exchange"
-
In the "Details" tab, we can see the list of scopes that requested during setup:
-
In the "Client #X" tab, we can "Inspect" the token.
So, it appears that every component (from Azure app registration, to the client's request configuration, to the received token data, to the decoded JWT token data) reports IMAP.AccessAsUser.All
as an included scope.
There is a subtle discrepancy in how the scope is labeled - sometimes, it's presented as the shorter IMAP.AccessAsUser.All
; other times, it's the longer https://outlook.office.com/IMAP.AccessAsUser.All
. To be sure, I've requested tokens with both notations -- and in both cases, the outcome is the same. (To wit: the JWT token has IMAP.AccessAsUser.All
- but it does not work for IMAP access.)
(Theory 1.3) The OAuth2 client obtains a valid token with proper scopes - but it doesn't speak IMAP-XOAuth2 correctly.
I've tested this theory a few ways:
- Connect to another service (Google Mail) which uses the same protocol (IMAP-XOauth2). It works.
- Hack the PHP IMAP library (
imap_transport.php
) to inspect the wire-communication. Manually decode the XOAuth2 statement to ensure that it follows the XOAuth2 formula. - Write a new script with a different PHP IMAP library to try the same token. The alternate library works with Google Mail but not Microsoft Exchange.
(Theory 2.1) There is a problem in the account flags/configuration.
As I mentioned above (theory 1.2), the "App registration" has all the scopes described in the docs
In the "Exchange Administration Center", the account has IMAP enabled:
In the Exchange webmail UI, there is a settings page with a search-box. Search for "IMAP", and it shows:
It's peculiar that searching for "IMAP" presents an inert radio-button for POP, but it does show connection details for IMAP.
TBH, Microsoft's backend here is labyrinthine, so it's hard to be sure that I've checked every possible permission. But I can't find anything else that would prevent IMAP access.
Perhaps someone with more experience in Microsoft's backend should try to run on a different account? Perhaps an account that has been demonstrated to work with IMAP?
(Theory 2.2) There is a bug in the OAuth2 service provider.
I can't really prove or disprove this theory. I don't really want to believe this is the problem (surely this stuff works for other people...). OTOH, maybe it's actually quite rare for people to use IMAP+XOAuth2 with Exchange Online...