Laravel Microsoft Graph API

A Laravel package for working with Microsoft Graph API.

https://github.com/daveismyname/laravel-microsoft-graph

MsGraph comes in two flavours:

  • MsGraph: login in as a user.
  • MsGraphAdmin: login as a tenant (administrator) useful for running background tasks.

Microsoft Graph API documentation can be found at:
https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/beta-overview

Application Register

To use Microsoft Graph API an application needs creating at https://apps.dev.microsoft.com

Create a new application, name the application. Click continue the Application Id will then be displayed.

Next click Generate New Password under Application Secrets it won't be shown again so ensure you've copied it and added to .env more details further down.


MSGRAPH_CLIENT_ID=
MSGRAPH_SECRET_ID=

Now click Add Platform under Platforms and select web.

Enter you desired redirect url. This is the url your application will use to connect to Graph API.

Now under Microsoft Graph Permissions click add and select which permissions to use, a maximum of 20 can be selected.

The other options are optional, click save at the bottom of the page to save your changes.

Install

Via Composer

composer require daveismyname/laravel-msgraph

In Laravel 5.5 the service provider will automatically get registered. In older versions of the framework just add the service provider in config/app.php file


'providers' => [
    // ...
    Daveismyname\MsGraph\MsGraphServiceProvider::class,
];

Config

You can publish the config file with:

php artisan vendor:publish --provider="Daveismyname\MsGraph\MsGraphServiceProvider" --tag="config"

When published, the config/msgraph.php config file contains:


return [

    /*
    * the clientId is set from the Microsoft portal to identify the application
    * https://apps.dev.microsoft.com
    */
    'clientId' => env('MSGRAPH_CLIENT_ID'),

    /*
    * set the application secret
    */

    'clientSecret' => env('MSGRAPH_SECRET_ID'),

    /*
    * Set the url to trigger the oauth process this url should call return MsGraph::connect();
    */
    'redirectUri' => env('MSGRAPH_OAUTH_URL'),

    /*
    * set the url to be redirected to once the token has been saved
    */

    'msgraphLandingUri'  => env('MSGRAPH_LANDING_URL'),

    /*
    set the tenant authorize url
    */

    'tenantUrlAuthorize' => env('MSGRAPH_TENANT_AUTHORIZE'),

    /*
    set the tenant token url
    */
    'tenantUrlAccessToken' => env('MSGRAPH_TENANT_TOKEN'),

    /*
    set the authorize url
    */

    'urlAuthorize' => 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',

    /*
    set the token url
    */
    'urlAccessToken' => 'https://login.microsoftonline.com/common/oauth2/v2.0/token',

    /*
    set the scopes to be used, Microsoft Graph API will accept up to 20 scopes
    */

    'scopes' => 'offline_access openid calendars.readwrite contacts.readwrite files.readwrite mail.readwrite mail.send tasks.readwrite mailboxsettings.readwrite user.readwrite',

    /*
    The default timezone is set to Europe/London this option allows you to set your prefered timetime
    */
    'preferTimezone' => env('MSGRAPH_PREFER_TIMEZONE', 'outlook.timezone="Europe/London"'),
];

Migration

You can publish the migration with:


php artisan vendor:publish --provider="Daveismyname\MsGraph\MsGraphServiceProvider" --tag="migrations"

After the migration has been published you can create the tokens tables by running the migration:


php artisan migrate

.ENV Configuration

Ensure you've set the following in your .env file:


MSGRAPH_CLIENT_ID=
MSGRAPH_SECRET_ID=

MSGRAPH_OAUTH_URL=https://domain.com/msgraph/oauth
MSGRAPH_LANDING_URL=https://domain.com/msgraph

When logging in as a tenant add the tenant ID .env:


MSGRAPH_TENANT_AUTHORIZE=https://login.microsoftonline.com/{tenant_id}/adminconsent
MSGRAPH_TENANT_TOKEN=https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token

To find your Office 365 tenant ID in the Azure AD admin center

  1. Sign in to the Azure Active Directory admin center https://aad.portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Overview as a global or user management admin.
  2. Under Manage, select Properties. The tenant ID is shown in the Directory ID box.

Optionally add


MSGRAPH_PREFER_TIMEZONE='outlook.timezone="Europe/London"'

Usage for MsGraph

Note this package expects a user to be logged in.

A routes example:


Route::group(['middleware' => ['web', 'auth']], function(){
    Route::get('msgraph', function(){

        if (! is_string(MsGraph::getAccessToken())) {
            return redirect(env('MSGRAPH_OAUTH_URL'));
        } else {
            //display your details
            return MsGraph::get('me');
        }

    });

    Route::get('msgraph/oauth', function(){
        return MsGraph::connect();
    });
});

Or using a middleware route, if user does not have a graph token then automatically redirect to get authenticated:


Route::group(['middleware' => ['web', 'MsGraphAuthenticated']], function(){
    Route::get('msgraph', function(){
        return MsGraph::get('me');
    });
});

Route::get('msgraph/oauth', function(){
    return MsGraph::connect();
});

Once authenticated you can call MsGraph:: with the following verbs:


MsGraph::get($endpoint, $array = [], $id = null)
MsGraph::post($endpoint, $array = [], $id = null)
MsGraph::put($endpoint, $array = [], $id = null)
MsGraph::patch($endpoint, $array = [], $id = null)
MsGraph::delete($endpoint, $array = [], $id = null)

The second param of array is not always required, its requirement is determined from the endpoint being called, see the API documentation for more details.

The third param $id is optional when used the access token will be attempted to be retrieved based on the id. When omitted the logged in user will be used.

These expect the API endpoints to be passed, the url https://graph.microsoft.com/beta/ is provided, only endpoints after this should be used ie:


MsGraph::get('me/messages')

Middleware

To restrict access to routes only to authenticated users there is a middleware route called MsGraphAuthenticated

Add MsGraphAuthenticated to routes to ensure the user is authenticated:


Route::group(['middleware' => ['web', 'MsGraphAuthenticated'], function()

To access token model reference this ORM model:


use Daveismyname\MsGraph\Models\MsGraphToken;

Usage for MsGraphAdmin

Only administrators can login as tenants.

A routes example:


Route::group(['middleware' => ['web', 'auth']], function(){
    Route::get('msgraph', function(){

        if (! is_string(MsGraphAdmin::getAccessToken())) {
            return redirect(env('MSGRAPH_OAUTH_URL'));
        } else {
            //display all users
            return MsGraphAdmin::get('users');
        }

    });

    Route::get('msgraph/oauth', function(){
        return MsGraphAdmin::connect();
    });
});

Or using a middleware route, if user does not have a graph token then automatically redirect to get authenticated:


Route::group(['middleware' => ['web', 'MsGraphAdminAuthenticated']], function(){
    Route::get('msgraph', function(){
        //fetch all users
        return MsGraphAdmin::get('users');
    });
});

Route::get('msgraph/oauth', function(){
    return MsGraphAdmin::connect();
});

Once authenticated you can call MsGraph:: with the following verbs:


MsGraphAdmin::get($endpoint, $array = [])
MsGraphAdmin::post($endpoint, $array = [])
MsGraphAdmin::put($endpoint, $array = [])
MsGraphAdmin::patch($endpoint, $array = [])
MsGraphAdmin::delete($endpoint, $array = [])

The second param is array is not always required, its requirement is determined from the endpoint being called, see the API documentation for more details.

These expect the API endpoints to be passed, the url https://graph.microsoft.com/beta/ is provided, only endpoints after this should be used ie:


MsGraphAdmin::get('users')

Middleware

To restrict access to routes only to authenticated users there is a middleware route called MsGraphAdminAuthenticated

Add MsGraphAdminAuthenticated to routes to ensure the user is authenticated:


Route::group(['middleware' => ['web', 'MsGraphAdminAuthenticated'], function()

To access token model reference this ORM model:


use Daveismyname\MsGraph\Models\MsGraphToken;

Contacts


MsGraphAdmin provides a clean way of working with a users contacts.

To work with contacts first call ->contacts() followed by a method.

Each call needs to use ->userid($userid) to inform the object which user to work with.


MsGraphAdmin::contacts()->userid($userId)


List Contacts

Return a list of all contacts


MsGraphAdmin::contacts()->userid($userId)->get()

By default only 10 contacts are returned this can be changed by either using GET requests or pass an array of option to get()


Option 1: GET Request

Adding top=50 to the url will return 50 contacts, to skip the starting point use skip=2 to start from the 2nd set. These can be used together:


?top=50&skip=2

Option 2: Array

The default array that is used internally is below, you can override these options by passing an array to the ->get() method.


[
    "\$orderby" => "displayName",
    "\$top" => $top,
    "\$skip" => $skip,
    "\$count" => "true",
]

This would look like this:


MsGraphAdmin::contacts()->userid($userId)->get([
    "\$orderby" => "displayName",
    "\$top" => 100,
    "\$skip" => 0,
    "\$count" => "true",
]);

The response returned is an array in this format:


array:4 [
  "contacts" => array:4 [
    "@odata.context" => "https://graph.microsoft.com/beta/$metadata#users('5b7f8791-03a1-4b68-9ff2-5bdca45563')/contacts"
    "@odata.count" => 1112
    "@odata.nextLink" => "https://graph.microsoft.com/beta/users/5b7f8791-03a1-4b68-9ff2-5bdca45563/contacts?$orderby=displayName&$top=50&$skip=52&$count=true"
    "value" => array:50 [
        0 => array:38 [
        "@odata.etag" => "W/"BYAAAALc1XGBA2GTZfCUzLE1FjyAAKvR6Gl""
        "id" => "hhOTJlODA2LTUwYTQtNGNmMS1hZWQ2LWVjZjU1OTZiYzY4OQBGAAAAAAAbbv6dt9gvS71-sGvg9qUVBwALc1XGBA2GTZfCUzLE1FjyAAAAui-HAAALc1XGBA2GTZfCUzLE1FjyAAEEO4cvAAA="
        "createdDateTime" => "2017-06-15T21:47:53Z"
        "lastModifiedDateTime" => "2019-04-04T21:26:50Z"
        "changeKey" => "EQAAABYAAAALc1XGBA2GTZfCUzLE1FjyAAKvR6Gl"
        "categories" => []
        "parentFolderId" => "TJlODA2LTUwYTQtNGNmMS1hZWQ2LWVjZjU1OTZiYzY4OQAuAAAAAAAbbv6dt9gvS71-sGvg9qUVAQALc1XGBA2GTZfCUzLE1FjyAAAAui-HAAA="
        "birthday" => null
        "fileAs" => ""
        "displayName" => "​John Smith"
        "givenName" => null
        "initials" => null
        "middleName" => null
        "nickName" => null
        "surname" => null
        "title" => null
        "yomiGivenName" => null
        "yomiSurname" => null
        "yomiCompanyName" => null
        "generation" => null
        "imAddresses" => []
        "jobTitle" => null
        "companyName" => null
        "department" => null
        "officeLocation" => null
        "profession" => null
        "assistantName" => null
        "manager" => null
        "spouseName" => null
        "personalNotes" => ""
        "children" => []
        "gender" => null
        "isFavorite" => null
        "emailAddresses" => array:1 [
             0 => array:3 [
                "type" => "unknown"
                "name" => "​John Smith"
                "address" => "j.smith@domain.co.uk"
              ]
            ]
        ]
        "websites" => []
        "phones" => []
        "postalAddresses" => []
        "flag" => array:1 [
          "flagStatus" => "notFlagged"
        ]
      ]
    ]
  ]
  "total" => 1112
  "top" => "50"
  "skip" => "52"
]

The @odata.nextLink is the link for the next set of data that can be used directly or make use of the top and skip that are returned.



Create Contacts

To create a contact call ->store($data) and pass in an array of details for the contact.

To see all contact properties look at https://docs.microsoft.com/en-us/graph/api/resources/contact?view=graph-rest-1.0


$data = [
    'displayName' => 'John Smith',
    'givenName' => 'John Smith',
    'emailAddresses' => [
        [
            'address' => 'j.smith@domain.com',
            'name' => 'John Smith'
        ]
    ]
];
MsGraphAdmin::contacts()->userid($userId)->store($data)


View Contact

To view a contact call ->find($id) followed by the id of the contact.


MsGraphAdmin::contacts()->userid($userId)->find($id)


Update Contact

To update a contact call ->update($data) and pass in the id of the contact and an array of details to be changed.

To see all contact properties look at https://docs.microsoft.com/en-us/graph/api/resources/contact?view=graph-rest-1.0

In this example only the email address will be changed


$data = [
    'emailAddresses' => [
        [
            'address' => 'j.agent@domain.com',
            'name' => 'John Agent'
        ]
    ]
];
MsGraphAdmin::contacts()->userid($userId)->update($id, $data)


Delete Contact

To delete a contact call ->delete($id) followed by the id of the contact.


MsGraphAdmin::contacts()->userid($userId)->delete($id)

Emails


MsGraphAdmin provides a clean way of working with a users emails.

To work with emails first call ->emails() followed by a method.

Each call needs to use ->userid($userid) to inform the object which user to work with.


MsGraphAdmin::emails()->userid($userId)


List emails

Return a list of emails


MsGraphAdmin::emails()->userid($userId)->get()

By default only 10 emails are returned this can be changed by either using GET requests or pass an array of option to get()


Option 1: GET Request

Adding top=50 to the url will return 50 emails, to skip the starting point use skip=2 to start from the 2nd set. These can be used together:


?top=50&skip=2

Option 2: Array

The default array that is used internally is below, you can override these options by passing an array to the ->get() method.


[
    "\$orderby" => "displayName",
    "\$top" => $top,
    "\$skip" => $skip,
    "\$count" => "true",
]

This would look like this:


MsGraphAdmin::emails()->userid($userId)->get([
    "\$orderby" => "displayName",
    "\$top" => 15,
    "\$skip" => 0,
    "\$count" => "true",
]);

The response returned is an array in this format:


array:4 [
  "emails" => array:4 [
    "@odata.context" => "https://graph.microsoft.com/beta/$metadata#users('5b7f8791-03a1-4b68-9ff2-5bdca45563')/messages"
    "@odata.count" => 44177
    "@odata.nextLink" => "https://graph.microsoft.com/beta/users/5b7f8791-03a1-4b68-9ff2-5bdca45563/messages?$count=true&$orderby=sentDateTime+desc&$skip=15"
    "value" => [
        "@odata.etag" => "W/"CQAAABYAAAC8b+tAO4nLRZCbkhud5CXFAASVGY/p""
        "id" => "AAMkADdlZTBjNjQ4LWI0OGItNDFhZS05ZDNiLThiY2JkYzIzZWZkYwBGAAAAAABFX7lJCx7ZRLTJ6iI0yZK6BwC8b_tAO4nLRZCbkhud5CXFAAAAAAELAAC8b_tAO4nLRZCbkhud5CXFAASUuapzAAA="
        "createdDateTime" => "2019-05-29T08:58:09Z"
        "lastModifiedDateTime" => "2019-05-29T09:02:00Z"
        "changeKey" => "CQAAABYAAAC8b+tAO4nLRZCbkhud5CXFAASVGY/p"
        "categories" => []
        "receivedDateTime" => "2019-05-29T08:58:09Z"
        "sentDateTime" => "2019-05-29T08:58:04Z"
        "hasAttachments" => false
        "internetMessageId" => ""
        "subject" => "sent you a document to sign"
        "bodyPreview" => You Have Been Sent A Document To Sign
        "importance" => "normal"
        "parentFolderId" => "AQMkADdlZQAwYzY0OC1iNDhiLTQxYWUtOWQzYi04YmNiZGMyM2VmZGMALgAAA0VfuUkLHtlEtMnqIjTJkroBALxv60A7ictFkJuSG53kJcUAAAIBCwAAAA=="
        "conversationId" => "AAQkADdlZTBjNjQ4LWI0OGItNDFhZS05ZDNiLThiY2JkYzIzZWZkYwAQADNDbfE-oxVGsAHIKhk2vCE="
        "conversationIndex" => "AQHVFfyjM0Nt8T+jFUawAcgqGTa8IQ=="
        "isDeliveryReceiptRequested" => null
        "isReadReceiptRequested" => false
        "isRead" => true
        "isDraft" => false
        "webLink" => "https://outlook.office365.com/owa/?ItemID=jQ4LWI0OGItNDFhZS05ZDNiLThiY2JkYzIzZWZkYwBGAAAAAABFX7lJCx7ZRLTJ6iI0yZK6BwC8b%2BtAO4nLRZCbkhud5CXFAAAAAAEL ▶"
        "inferenceClassification" => "other"
        "unsubscribeData" => []
        "unsubscribeEnabled" => false
        "mentionsPreview" => null
        "body" => array:2 [▶]
        "sender" => array:1 [▼
          "emailAddress" => array:2 [▼
            "name" => "via Signable"
            "address" => "document@signable.co.uk"
          ]
        ]
        "from" => array:1 [▼
          "emailAddress" => array:2 [▼
            "name" => "via Signable"
            "address" => "document@signable.co.uk"
          ]
        ]
        "toRecipients" => array:1 [▼
          0 => array:1 [▼
            "emailAddress" => array:2 [▶]
          ]
        ]
        "ccRecipients" => []
        "bccRecipients" => []
        "replyTo" => array:1 [▼
          0 => array:1 [▼
            "emailAddress" => array:2 [▼
              "name" => "John Smith"
              "address" => "j.smith@domain.co.uk"
            ]
          ]
        ]
        "flag" => array:1 [▼
          "flagStatus" => "notFlagged"
        ]
    ]
  "total" => 44177
  "top" => "0"
  "skip" => "15"
]

The @odata.nextLink is the link for the next set of data that can be used directly or make use of the top and skip that are returned.



Send Email

To send an email the format is different to normal calls. The format is to call multiple methods to set the email properties.

Required methods are: to(array) subject(string) body(string/markup) send()

Note these methods expect an array to be passed:

  • to(['email@domains.com'])
  • cc(['email@domains.com'])
  • bcc(['email@domains.com'])
  • attachments(['path/to/file'])

MsGraphAdmin::emails()
->userid($userId)
->to(['email@domains.com'])
->subject('the subject')
->body('

the content

') ->send()

cc() and bcc and attachments are optional.

To send attachments pass an array of files paths


MsGraphAdmin::emails()
->userid($userId)
->to(['email@domains.com'])
->subject('the subject')
->body('

the content

') ->attachments([public_path('images/logo')]) ->send()


Read Email

To view an email call ->find($id) followed by the id of the email.


MsGraphAdmin::emails()->userid($userId)->find($id)


Reply Email

To reply to an email call ->reply() and use ->comment() instead of ->body().


MsGraphAdmin::emails()
->userid($userId)
->id($id)
->to(['email@domains.com'])
->subject('the subject')
->comment('

the reply content

') ->reply()


Forward Email

To forward to an email call ->forward() and use ->comment() instead of ->body().


MsGraphAdmin::emails()
->userid($userId)
->id($id)
->to(['email@domains.com'])
->subject('the subject')
->comment('

the reply content

') ->forward()

Delete Email

To delete an email call ->delete($id) followed by the id of the email.


MsGraphAdmin::emails()->userid($userId)->delete($id)