PHP: Mailing through Office 365 using the Graph API
A while ago I needed to update my PHP applications mail handing scripts, as Microsoft are disabling basic authentication and they connected using EWS with basic authentication. I took the opportunity to update them to use the Microsoft Graph API instead.
My systems generally run mail as a background process, e.g. sending/receiving mail to the helpdesk mailbox, so this article is written in that vein. If you wanted to access mail in an interactive way (so the user is sat in front of the browser at the time mail is accessed) you'd need to switch to Delegated rights, rather than Application, and the user would log in rather than the script logging in. I've not looked at this so not able to say much more about it.
First of all you'll need to create an application in Azure AD - you'll need to have the appropriate permissions in Azure as we need to give admin consent:
- Go to https://portal.azure.com and navigate to Azure Active Directory > App Registrations.
- Click on New Registration, enter your application name and select which directories you want to be able to access.
- Now you'll see a screen listing some IDs - take down the Client ID and Directory (Tenant) ID.
- Go to the Certificates and Secrets page, and add a client secret - note this down too.
- Now go to the API Permissions page. Click on Add a Permission, Microsoft Graph, Application Permissions, then select User.ReadAll, Mail.ReadWrite and Mail.Send. Now click Add Permissions. Note these are Application permissions, not Delegated permissions
- You'll now need to give admin approval for these permissions - so click on Grant Admin consent.
Your application will now have permission to read and write all mailboxes, and send as any user. We can now restrict this to specific mailboxes with the following powershell - e.g. my helpdesk system is restricted to only look at the helpdesk mailboxes:
New-ApplicationAccessPolicy -AppId clientID -PolicyScopeGroupId [email protected] -AccessRight RestrictAccess
You can run that multiple times for multiple mailboxes, or specify a group instead of a single mailbox
You can test with:
Test-ApplicationAccessPolicy -AppId clientID -Identity [email protected]
This will show Granted if you test the mailbox you just gave permissions to, and Denied for anything else. It sometimes takes up to half an hour for permission changes to take effect.
I'll go through using the script now, and put the script itself at the end of the page.
First of all you'll need to include the script and create a new object:
require_once 'mailer_graph.php';
$graphMailer = new graphMailer('tenantid','clientid','secret');
You'll need to pass the directory (tenant) ID, client ID and the client secret that you noted down earlier. If all is well the script will authenticate itself using oAuth and obtain a token.
Receiving messages - this will by default grab the top 10 messages in the mailbox. I've not put any filters in as I tend to grab the mail, process it (e.g. into the helpdesk tickets database) and then delete the corresponding email. I just cycle through this until there are 0 emails left.
$messages = $graphMailer->getMessages('[email protected]');
echo '<pre>';
print_r($messages);
echo '</pre>';
You can set filters by altering line 37 in the script, e.g.
$messageList = json_decode($this->sendGetRequest($this->baseURL . 'users/' . $mailbox . '/mailFolders/Inbox/Messages?$top=20'));
Details of filtering can be found at https://docs.microsoft.com/en-us/graph/api/user-list-messages?view=graph-rest-1.0&tabs=http
To send an email, you need to prepare an array:
$mailArgs = array('subject' => 'Test message',
'replyTo' => array('name' => 'Katy', 'address' => '[email protected]'),
'toRecipients' => array(
array('name' => 'Neil', 'address' => '[email protected]'),
array('name' => 'Someone', 'address' => '[email protected]')
), // name is optional
'ccRecipients' => array(
array('name' => 'Neil', 'address' => '[email protected]'),
array('name' => 'Someone', 'address' => '[email protected]')
), // name is optional, otherwise array of address=>email@address
'importance' => 'normal',
'conversationId' => '', //optional, use if replying to an existing email to keep them chained properly in outlook
'body' => '<html>Blah blah blah</html>',
'images' => array(
array('Name' => 'blah.jpg', 'ContentType' => 'image/jpeg', 'Content' => 'results of file_get_contents(blah.jpg)', 'ContentID' => 'cid:blah')
), //array of arrays so you can have multiple images. These are inline images. Everything else in attachments.
'attachments' => array(
array('Name' => 'blah.pdf', 'ContentType' => 'application/pdf', 'Content' => 'results of file_get_contents(blah.pdf)')
)
)
$graphMailer->sendMail('[email protected]', $mailArgs);
Then call sendMail and pass the mailbox you wish to send from, and the array.
To delete a message, you'll need to get the message ID (this is one of the fields returned when you run getMessages). You can just move the email to deleted items:
$graphMailer->deleteEmail('[email protected]', messageID);
or delete it permanently. This won't be available in deleted items, or any of the recovery stages such as 2nd stage deleted items:
$graphMailer->deleteEmail('[email protected]', messageID, false);
With any luck, you'll be sending and receiving mail!
The script is now located in my GitHub repository.