0

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

Ash
  • 21
  • 2

2 Answers2

0

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.

Glen Scales
  • 20,495
  • 1
  • 20
  • 23
  • Thank you for answering. I tried to update the Manifest with the details that I got from https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth but am getting this error "Failed to update UPilot local application. Error detail: The Required Resource Access specified in the request is invalid. Please check the resource ids and the permission ids and try again. [6pfyR7SvXS5Dv4hBRcM8Fd] " – Ash Nov 03 '21 at 07:07
  • Try adding it through the Portal if you select "APIs my organization uses" and then select Office 365 Exchange Online – Glen Scales Nov 03 '21 at 22:47
  • Tried all possible things that I could. But am getting the same error. – Ash Nov 12 '21 at 06:45
  • Fatal error: Uncaught garethp\ews\API\Exception\UnauthorizedException in C:\xampp\htdocs\exchange\vendor\garethp\php-ews\src\API\ExchangeWebServices.php:454 Stack trace: #0 C:\xampp\htdocs\exchange\vendor\garethp\php-ews\src\API\ExchangeWebServices.php(367): garethp\ews\API\ExchangeWebServices->handleNonSuccessfulResponses(NULL, 401) – Ash Nov 12 '21 at 06:48
0

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.