UnauthorizedException when connecting to exchange accounts using oauth2 - garethp - php-ews

854 Views Asked by At

I use garethp php-ews to connect EWS using oauth2. I am receiving the access token but when passing it to the mail function (getMailItems,getMailbox,getFolder....etc) following fatal error with "UnauthorizedException" is showing. Tried many ways but still the same.

Fatal error: Uncaught garethp\ews\API\Exception\UnauthorizedException in C:\xampp\htdocs\exchange\vendor\garethp\php-ews\src\API\ExchangeWebServices.php:453 Stack trace: #0 C:\xampp\htdocs\exchange\vendor\garethp\php-ews\src\API\ExchangeWebServices.php(368): garethp\ews\API\ExchangeWebServices->handleNonSuccessfulResponses(NULL, 401) #1 C:\xampp\htdocs\exchange\vendor\garethp\php-ews\src\API\ExchangeWebServices.php(301): garethp\ews\API\ExchangeWebServices->processResponse(NULL) #2 C:\xampp\htdocs\exchange\vendor\garethp\php-ews\src\API.php(362): garethp\ews\API\ExchangeWebServices->__call('GetFolder', Array) #3 C:\xampp\htdocs\exchange\vendor\garethp\php-ews\src\API.php(378): garethp\ews\API->getFolder(Array) #4 C:\xampp\htdocs\exchange\vendor\garethp\php-ews\src\Mail\MailAPI.php(22): garethp\ews\API->getFolderByDistinguishedId('inbox') #5 C:\xampp\htdocs\exchange\vendor\garethp\php-ews\src\Mail\MailAPI.php(81): garethp\ews\Mail\MailAPI->getFolderId() #6 C:\xampp\htdocs\exchange\testSync.php(50): garethp\ews\Mail\MailAPI in C:\xampp\htdocs\exchange\vendor\garethp\php-ews\src\API\ExchangeWebServices.php on line 453

Here are the parameters am passing :

 $tokenEndpoint = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
 $authorizationEndpoint = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize';
 $clientId = '********';
 $clientSecret = '********';
 $redirectUri = 'http://localhost/testredirect.php';
 $scope = 'https://outlook.office.com/Mail.Read';

Also tried with graph API : $scope = 'https://graph.microsoft.com/Mail.Read';

This is the API permissions I have : enter image description here

2

There are 2 best solutions below

4
Glen Scales On

There are only two scopes that work for EWS for delegate access this is

  • EWS.AccessAsUser.All

For App Only (Client credentials flow)

  • full_access_as_app

see https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth

EWS doesn't support the more constrained scopes that the Microsoft Graph supports which are the permissions your referencing and from a security point of view is the reason you would generally choose the Graph over EWS.

0
Dominik Mayrhofer On

I had a similar problem connecting to exchange accounts using the account token. It turned out, that the CURL behind the Requests need to have the following additional parameters if you work with tokens:

$headers[] = sprintf("Authorization: Bearer %s", $this->token);
curl_setopt($this->ch, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);

The Scope and Host that we used for the access token:

private const API_SCOPE = 'offline_access EWS.AccessAsUser.All EAS.AccessAsUser.All'; // Scope for Exchange Access
private const HOST_MICROSOFT_365 = 'outlook.office365.com'; // Host for Microsoft Connection

A full working example of a Request Method would be something like this:

public function __doRequest($request, $location, $action, $version, $one_way = 0){
    $headers = array(
        'Method: POST',
        'Connection: Keep-Alive',
        'User-Agent: PHP-SOAP-CURL',
        'Content-Type: text/xml; charset=utf-8',
        'SOAPAction: "'.$action.'"',
    );
    // Use Token for Authorization if set
    if(!empty($this->token)){
        $headers[] = sprintf("Authorization: Bearer %s", $this->token);
        curl_setopt($this->ch, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
    }else{ // User/Password Login
        curl_setopt($this->ch, CURLOPT_USERPWD, $this->user.':'.$this->password);
    }
    
    $this->ch = curl_init($location);

    curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($this->ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($this->ch, CURLOPT_POST, true );
    curl_setopt($this->ch, CURLOPT_POSTFIELDS, $request);
    curl_setopt($this->ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);

    curl_setopt($this->ch, CURLOPT_TIMEOUT, 60); //timeout in seconds
    curl_setopt($this->ch, CURLOPT_CONNECTTIMEOUT, 60);

    $response = curl_exec($this->ch);

    return $response;
}

Most classes that i found do not include the Bearer Authorization including CURLAUTH_BEARER. I found a fork from jamesiarmes php-ews library but that didn't work for me so i decided to implement it myself.