Skip to content
This repository was archived by the owner on Mar 13, 2020. It is now read-only.

Commit 1b3087f

Browse files
committed
Updated to send X-AnchorMailbox
1 parent 16c41ac commit 1b3087f

5 files changed

Lines changed: 79 additions & 19 deletions

File tree

‎README.md‎

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@ Create a new file to contain all of our OAuth functions called `oauth.php`. In t
8484
private static $authorizeUrl = '/common/oauth2/v2.0/authorize?client_id=%1$s&redirect_uri=%2$s&response_type=code&scope=%3$s';
8585
private static $tokenUrl = "/common/oauth2/v2.0/token";
8686

87-
// The app only needs Mail.Read
88-
private static $scopes = array("https://outlook.office.com/mail.read");
87+
// The app only needs openid (for user's ID info), and Mail.Read
88+
private static $scopes = array("openid",
89+
"https://outlook.office.com/mail.read");
8990
9091
public static function getLoginUrl($redirectUri, $scopes) {
9192
// Build scope string. Multiple scopes are separated
@@ -138,7 +139,7 @@ The class exposes one function for now, `getLoginUrl`. This function will genera
138139

139140
Save your work and copy the files to your web server. If you browse to `http://localhost/php-tutorial/home.php` and hover over the sign in link, it should look like this:
140141

141-
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=<SOME GUID>&redirect_uri=https%3A%2F%2Fcid.azurewebsites.net&response_type=code&scope=https%3A%2F%2Foutlook.office.com%2Fmail.read
142+
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=<SOME GUID>&redirect_uri=https%3A%2F%2Fcid.azurewebsites.net&response_type=code&scope=openid+https%3A%2F%2Foutlook.office.com%2Fmail.read
142143

143144
Clicking on the link will allow you to sign in and grant access to the app, but will then result in an error. Notice that your browser gets redirected to `http://localhost/php-tutorial/authorize.php`. That file doesn't exist yet. Don't worry, that's our next task.
144145

@@ -217,6 +218,33 @@ Now let's add a new function to the `oAuthService` class to retrieve a token. Ad
217218

218219
This function uses cURL to issue the access token request to login.microsoftonline.com. The first part of this function is building the payload of the request, then the rest is using cURL to issue a POST request to the OAuth2 token endpoint. Finally, the results are parsed into an array of values using `json_decode`.
219220

221+
### Getting the user's email address ###
222+
223+
The array of values returned doesn't only include the access token. Since we included the `openid` scope in our request, it also contains in ID token. We can use this token to find out a few pieces of information about the logged on user. In this case, we want to get the user's email address. You'll see why we want this soon.
224+
225+
Add a new function to the `oAuthService` class called `getUserEmailFromIdToken`.
226+
227+
#### New `getUserEmailFromIdToken` function in `./oauth.php` ####
228+
229+
public static function getUserEmailFromIdToken($idToken) {
230+
error_log("ID TOKEN: ".$idToken);
231+
232+
// JWT is made of three parts, separated by a '.'
233+
// First part is the header
234+
// Second part is the token
235+
// Third part is the signature
236+
$token_parts = explode(".", $idToken);
237+
238+
// We care about the token
239+
// URL decode first
240+
$token = strtr($token_parts[1], "-_", "+/");
241+
// Then base64 decode
242+
$jwt = base64_decode($token);
243+
// Finally parse it as JSON
244+
$json_token = json_decode($jwt, true);
245+
return $json_token['preferred_username'];
246+
}
247+
220248
Now replace the contents of the `./authorize.php` file with the following.
221249

222250
#### Updated contents of `./authorize.php` ####
@@ -232,7 +260,7 @@ Now replace the contents of the `./authorize.php` file with the following.
232260

233261
<p>Access Token: <?php echo $tokens['access_token'] ?></p>
234262

235-
Save your changes and restart the app. This time, after you sign in, you should see an access token displayed. Now let's update `./authorize.php` one more time to save the access token into a session variable and redirect back to the home page.
263+
Save your changes and restart the app. This time, after you sign in, you should see an access token displayed. Now let's update `./authorize.php` one more time to get the user's email address, save the access token and email address into session variables, and redirect back to the home page.
236264

237265
#### Updated contents of `./authorize.php` ####
238266

@@ -246,6 +274,10 @@ Save your changes and restart the app. This time, after you sign in, you should
246274
247275
if ($tokens['access_token']) {
248276
$_SESSION['access_token'] = $tokens['access_token'];
277+
278+
// Get the user's email from the ID token
279+
$user_email = oAuthService::getUserEmailFromIdToken($tokens['id_token']);
280+
$_SESSION['user_email'] = $user_email;
249281
250282
// Redirect back to home page
251283
header("Location: http://localhost/php-tutorial/home.php");
@@ -300,14 +332,15 @@ Let's start by adding a new file to contain all of our Mail API functions called
300332

301333
<?php
302334
class OutlookService {
303-
public static function makeApiCall($access_token, $method, $url, $payload = NULL) {
335+
public static function makeApiCall($access_token, $user_email, $method, $url, $payload = NULL) {
304336
// Generate the list of headers to always send.
305337
$headers = array(
306338
"User-Agent: php-tutorial/1.0", // Sending a User-Agent header is a best practice.
307339
"Authorization: Bearer ".$access_token, // Always need our auth token!
308340
"Accept: application/json", // Always accept JSON response.
309341
"client-request-id: ".self::makeGuid(), // Stamp each new request with a new GUID.
310-
"return-client-request-id: true" // Tell the server to include our request-id GUID in the response.
342+
"return-client-request-id: true", // Tell the server to include our request-id GUID in the response.
343+
"X-AnchorMailbox: ".$user_email // Provider user's email to optimize routing of API call
311344
);
312345
313346
$curl = curl_init($url);
@@ -393,15 +426,17 @@ Let's start by adding a new file to contain all of our Mail API functions called
393426
}
394427
?>
395428

396-
This function uses cURL to send the appropriate request to the specified endpoint, using the access token for authentication. We can use this function to call any of Outlook REST APIs. Let's add a new function to the `OutlookService` class to get the user's 10 most recent messages from the inbox.
429+
This function uses cURL to send the appropriate request to the specified endpoint, using the access token for authentication. It also uses the user's email address for an important optimization. By setting the `X-AnchorMailbox` header to the user's email address, the API endpoint can route API calls to the correct backend mailbox servers more efficiently.
430+
431+
We can use this function to call any of Outlook REST APIs. Let's add a new function to the `OutlookService` class to get the user's 10 most recent messages from the inbox.
397432

398-
In order to call our new `makeApiCall` function, we need an access token, a method, a URL, and an optional payload. We already have the access token, and from the [Mail API Reference](https://msdn.microsoft.com/office/office365/APi/mail-rest-operations#GetMessages), we know that the method to get messages is `GET` and that the URL to get messages is `https://outlook.office.com/api/v1.0/me/messages`. Using that information, add a `getMessages` function in `outlook.php`.
433+
In order to call our new `makeApiCall` function, we need an access token, the user's email address, a method, a URL, and an optional payload. We already have the access token, and from the [Mail API Reference](https://msdn.microsoft.com/office/office365/APi/mail-rest-operations#GetMessages), we know that the method to get messages is `GET` and that the URL to get messages is `https://outlook.office.com/api/v1.0/me/messages`. Using that information, add a `getMessages` function in `outlook.php`.
399434

400435
#### New `getMessages` function in `./outlook.php` ####
401436

402437
private static $outlookApiUrl = "https://outlook.office.com/api/v1.0";
403438

404-
public static function getMessages($access_token) {
439+
public static function getMessages($access_token, $user_email) {
405440
$getMessagesParameters = array (
406441
// Only return Subject, DateTimeReceived, and From fields
407442
"\$select" => "Subject,DateTimeReceived,From",
@@ -413,7 +448,7 @@ In order to call our new `makeApiCall` function, we need an access token, a meth
413448
414449
$getMessagesUrl = self::$outlookApiUrl."/Me/Messages?".http_build_query($getMessagesParameters);
415450
416-
return self::makeApiCall($access_token, "GET", $getMessagesUrl);
451+
return self::makeApiCall($access_token, $user_email, "GET", $getMessagesUrl);
417452
}
418453

419454
The function uses OData query parameters to do the following.
@@ -448,7 +483,7 @@ Update `./home.php` to call the `getMessages` function and display the results.
448483
<?php
449484
}
450485
else {
451-
$messages = OutlookService::getMessages($_SESSION['access_token']);
486+
$messages = OutlookService::getMessages($_SESSION['access_token'], $_SESSION['user_email']);
452487
?>
453488
<!-- User is logged in, do something here -->
454489
<p>Messages: <?php echo print_r($messages) ?></p>
@@ -490,7 +525,7 @@ Update `./home.php` one final time to generate the table.
490525
<?php
491526
}
492527
else {
493-
$messages = OutlookService::getMessages($_SESSION['access_token']);
528+
$messages = OutlookService::getMessages($_SESSION['access_token'], $_SESSION['user_email']);
494529
?>
495530
<!-- User is logged in, do something here -->
496531
<h2>Your messages</h2>

‎authorize.php‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
if ($tokens['access_token']) {
1111
$_SESSION['access_token'] = $tokens['access_token'];
1212

13+
// Get the user's email from the ID token
14+
$user_email = oAuthService::getUserEmailFromIdToken($tokens['id_token']);
15+
$_SESSION['user_email'] = $user_email;
16+
1317
// Redirect back to home page
1418
header("Location: http://localhost/php-tutorial/home.php");
1519
}

‎home.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<?php
2222
}
2323
else {
24-
$messages = OutlookService::getMessages($_SESSION['access_token']);
24+
$messages = OutlookService::getMessages($_SESSION['access_token'], $_SESSION['user_email']);
2525
?>
2626
<!-- User is logged in, do something here -->
2727
<h2>Your messages</h2>

‎oauth.php‎

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ class oAuthService {
77
private static $authorizeUrl = '/common/oauth2/v2.0/authorize?client_id=%1$s&redirect_uri=%2$s&response_type=code&scope=%3$s';
88
private static $tokenUrl = "/common/oauth2/v2.0/token";
99

10-
// The app only needs Mail.Read
11-
private static $scopes = array("https://outlook.office.com/mail.read");
10+
// The app only needs openid (for user's ID info), and Mail.Read
11+
private static $scopes = array("openid",
12+
"https://outlook.office.com/mail.read");
1213

1314
public static function getLoginUrl($redirectUri) {
1415
// Build scope string. Multiple scopes are separated
@@ -74,6 +75,25 @@ public static function getTokenFromAuthCode($authCode, $redirectUri) {
7475

7576
return $json_vals;
7677
}
78+
79+
public static function getUserEmailFromIdToken($idToken) {
80+
error_log("ID TOKEN: ".$idToken);
81+
82+
// JWT is made of three parts, separated by a '.'
83+
// First part is the header
84+
// Second part is the token
85+
// Third part is the signature
86+
$token_parts = explode(".", $idToken);
87+
88+
// We care about the token
89+
// URL decode first
90+
$token = strtr($token_parts[1], "-_", "+/");
91+
// Then base64 decode
92+
$jwt = base64_decode($token);
93+
// Finally parse it as JSON
94+
$json_token = json_decode($jwt, true);
95+
return $json_token['preferred_username'];
96+
}
7797
}
7898
?>
7999

‎outlook.php‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
class OutlookService {
44
private static $outlookApiUrl = "https://outlook.office.com/api/v1.0";
55

6-
public static function getMessages($access_token) {
6+
public static function getMessages($access_token, $user_email) {
77
$getMessagesParameters = array (
88
// Only return Subject, DateTimeReceived, and From fields
99
"\$select" => "Subject,DateTimeReceived,From",
@@ -15,17 +15,18 @@ public static function getMessages($access_token) {
1515

1616
$getMessagesUrl = self::$outlookApiUrl."/Me/Messages?".http_build_query($getMessagesParameters);
1717

18-
return self::makeApiCall($access_token, "GET", $getMessagesUrl);
18+
return self::makeApiCall($access_token, $user_email, "GET", $getMessagesUrl);
1919
}
2020

21-
public static function makeApiCall($access_token, $method, $url, $payload = NULL) {
21+
public static function makeApiCall($access_token, $user_email, $method, $url, $payload = NULL) {
2222
// Generate the list of headers to always send.
2323
$headers = array(
2424
"User-Agent: php-tutorial/1.0", // Sending a User-Agent header is a best practice.
2525
"Authorization: Bearer ".$access_token, // Always need our auth token!
2626
"Accept: application/json", // Always accept JSON response.
2727
"client-request-id: ".self::makeGuid(), // Stamp each new request with a new GUID.
28-
"return-client-request-id: true" // Tell the server to include our request-id GUID in the response.
28+
"return-client-request-id: true", // Tell the server to include our request-id GUID in the response.
29+
"X-AnchorMailbox: ".$user_email // Provider user's email to optimize routing of API call
2930
);
3031

3132
$curl = curl_init($url);

0 commit comments

Comments
 (0)