4

I'm currently trying to use the Office365 REST APIs, and for that, I need to auth using the OAuth2 mechanism. So far, so good.

After a long battle, I finally managed to fetch the said tokens, but I need to fetch the user information (such as his identifier, email, name, ...) that goes with the token.

Using the sandbox, I need something like that :

enter image description here

I already tried to fetch the user information through the api.office.com api :

curl https://api.office.com/discovery/v1.0/me/services -H "Authorization: Bearer <oauth token>" -H "Accept: application/json;odata=verbose" 

But all I have is the following :

{"error":{"code":"-2147024891, System.UnauthorizedAccessException","message":"Access denied. You do not have permission to perform this action or access this resource."}} 

the request to get said OAuth token is based on these parameters (through the HWIOAuthBundle) :

<?php $config = ['authorization_url' => 'https://login.windows.net/%s/oauth2/authorize', 'infos_url' => 'https://outlook.office365.com/api/%s/me', 'access_token_url' => 'https://login.windows.net/%s/oauth2/token', 'application' => 'common', 'api_version' => 'v1.0', 'csrf' => true]; $config['access_token_url'] = sprintf($config['access_token_url'], $config['application']); $config['authorization_url'] = sprintf($config['authorization_url'], $config['application']); $config['infos_url'] = sprintf($config['infos_url'], $config['api_version']); 

so any idea how could I get the user information (even the basic ones) ?

Thanks

-- edit

I think I got it. It would seems that with a curl request on https://outlook.office365.com/api/v1.0/me gives a simplistic array with the MailboxGuid, Id, Alias and GivenName :

curl https://outlook.office365.com/api/v1.0/me -X GET -H "Authorization: Bearer <my oauth token>" 

Gives the following (with sometimes a timeout, sometimes not... I guess I have to work on that, if anyone can have suggestions, feel free to barge in)

{"@odata.context":"https://outlook.office365.com/api/v1.0/$metadata#Me", "@odata.id":"https://outlook.office365.com/api/v1.0/Users('[email protected]')", "Id":"[email protected]", "DisplayName":"Baptiste Clavi\u00e9", "Alias":"baptiste", "MailboxGuid":"<snip>"} 

It is not as complete as the thing returned by the sandbox, but it should gives me what I need...

But... it sometimes gives me a timeout, sometimes it goes just fine... On a 3/5 ratio approximatively. Any idea ? Thanks

P.S : if you need to know how I configured the app on azure, do ask

1

2 Answers 2

2

Have you looked at the identity token that comes back from your token request? This might have all the info you want, and you already have it so no need to make a second request.

Here's a sample that (among other things) parses the identity token to get the user's display name. Check the getUserName function in Office365Service.php:

// Parses an ID token returned from Azure to get the user's // display name. public static function getUserName($id_token) { $token_parts = explode(".", $id_token); // First part is header, which we ignore // Second part is JWT, which we want to parse error_log("getUserName found id token: ".$token_parts[1]); // First, in case it is url-encoded, fix the characters to be // valid base64 $encoded_token = str_replace('-', '+', $token_parts[1]); $encoded_token = str_replace('_', '/', $encoded_token); error_log("After char replace: ".$encoded_token); // Next, add padding if it is needed. switch (strlen($encoded_token) % 4){ case 0: // No pad characters needed. error_log("No padding needed."); break; case 2: $encoded_token = $encoded_token."=="; error_log("Added 2: ".$encoded_token); break; case 3: $encoded_token = $encoded_token."="; error_log("Added 1: ".$encoded_token); break; default: // Invalid base64 string! error_log("Invalid base64 string"); return null; } $json_string = base64_decode($encoded_token); error_log("Decoded token: ".$json_string); $jwt = json_decode($json_string, true); error_log("Found user name: ".$jwt['name']); return $jwt['name']; } 
Sign up to request clarification or add additional context in comments.

5 Comments

There is indeed the identity_token, but it is just a token, so I don't what I should do with that. I tried a curl with its value, but it seems it doesn't return anything ?
The token needs to be parsed and then you can pull fields out of it. As it happens, I just published a PHP sample today that does this :) I'll add the information to my answer.
Nice, I'll check this up asap :)
Works great ! Now, all I have to check is the meaning of the fields. Would you happen to have a piece of documentation which refers which fields correspond to what (all the upn, oid, ... etc) ? Thanks
Sure do! msdn.microsoft.com/en-us/library/azure/dn645542.aspx. Check the "Access Token Response" section. There's a table of claims there introduced by the text "The id_token parameter value is a JSON web token (JWT) that includes the following claim types."
0

By looking many sources around.. I improved the php office365 client here

https://github.com/mehmetsen80/office365client

Assume you have a config such as this:

Config.php

<?php /** * Created by PhpStorm. * User: msen * Date: 3/10/16 * Time: 11:55 AM */ global $apiConfig; $apiConfig = array( 'oauth2_client_id' => '',//assign your own office 365 app client id 'oauth2_secret' => '', // Generate key from Azure Management Portal 'oauth2_redirect' => 'http://website.com/office365client/oauth2.php', //example url 'state' => '45d12e60b-8457-4d99-b20f-cfb612d1a138', //any unquiue key to Check against CSRF attack 'resource' => 'https://outlook.office365.com', 'oauth2_auth_url' => 'https://login.windows.net/common/oauth2/authorize', 'oauth2_token_url' => 'https://login.windows.net/common/oauth2/token', ); ?> 

At the end you'll have such a code..

session_start(); $client = new Office365_Client(); $forward_url = $client->createAuthUrl(); $code = $_GET['code']; if(isset($_GET['code'])) { $client->setCode($code); $client->fetchTokens(); //get tokens $client->fetchJWT();//let's get user info //put the user token info into sessions $_SESSION['name'] = $client->getJwt()->getName();//full name of the user $_SESSION['unique_name'] = $client->getJwt()->getUniqueName();//could be email or id from office365 $_SESSION['tid'] = $client->getJwt()->getTid();//tenant id }else{ header( 'Location: '.$forward_url ); //redirect automatically on the first page visit, 2nd page visit will get the $code } 

Microsoft tells us what user info can be retrieved.. https://msdn.microsoft.com/library/office/dn707383.aspx

JWT.php

 <?php /** * Created by PhpStorm. * User: msen * Date: 3/10/16 * Time: 12:04 PM */ class JWT { private $aud; private $iss; private $iat; private $nbf; private $exp; private $ver; private $tid; private $amr; private $oid; private $upn; private $puid; private $sub; private $given_name; private $family_name; private $name; private $unique_name; private $appid; private $appidacr; private $scp; private $acr; public function __construct($jwt_arr){ $this->aud = $jwt_arr['aud']; $this->iss = $jwt_arr['iss']; $this->iat = $jwt_arr['iat']; $this->nbf = $jwt_arr['nbf']; $this->exp = $jwt_arr['exp']; $this->ver = $jwt_arr['ver']; $this->tid = $jwt_arr['tid']; $this->amr = $jwt_arr['amr']; $this->oid = $jwt_arr['oid']; $this->upn = $jwt_arr['upn']; $this->puid = $jwt_arr['puid']; $this->sub = $jwt_arr['sub']; $this->given_name = $jwt_arr['given_name']; $this->family_name = $jwt_arr['family_name']; $this->name = $jwt_arr['name']; $this->unique_name = $jwt_arr['unique_name']; $this->appid = $jwt_arr['appid']; $this->appidacr = $jwt_arr['appidacr']; $this->scp = $jwt_arr['scp']; $this->acr = $jwt_arr['acr']; } /** * @return mixed */ public function getAud() { return $this->aud; } /** * @param mixed $aud */ public function setAud($aud) { $this->aud = $aud; } /** * @return mixed */ public function getIss() { return $this->iss; } /** * @param mixed $iss */ public function setIss($iss) { $this->iss = $iss; } /** * @return mixed */ public function getIat() { return $this->iat; } /** * @param mixed $iat */ public function setIat($iat) { $this->iat = $iat; } /** * @return mixed */ public function getNbf() { return $this->nbf; } /** * @param mixed $nbf */ public function setNbf($nbf) { $this->nbf = $nbf; } /** * @return mixed */ public function getExp() { return $this->exp; } /** * @param mixed $exp */ public function setExp($exp) { $this->exp = $exp; } /** * @return mixed */ public function getVer() { return $this->ver; } /** * @param mixed $ver */ public function setVer($ver) { $this->ver = $ver; } /** * @return mixed */ public function getTid() { return $this->tid; } /** * @param mixed $tid */ public function setTid($tid) { $this->tid = $tid; } /** * @return mixed */ public function getAmr() { return $this->amr; } /** * @param mixed $amr */ public function setAmr($amr) { $this->amr = $amr; } /** * @return mixed */ public function getOid() { return $this->oid; } /** * @param mixed $oid */ public function setOid($oid) { $this->oid = $oid; } /** * @return mixed */ public function getUpn() { return $this->upn; } /** * @param mixed $upn */ public function setUpn($upn) { $this->upn = $upn; } /** * @return mixed */ public function getPuid() { return $this->puid; } /** * @param mixed $puid */ public function setPuid($puid) { $this->puid = $puid; } /** * @return mixed */ public function getSub() { return $this->sub; } /** * @param mixed $sub */ public function setSub($sub) { $this->sub = $sub; } /** * @return mixed */ public function getGivenName() { return $this->given_name; } /** * @param mixed $given_name */ public function setGivenName($given_name) { $this->given_name = $given_name; } /** * @return mixed */ public function getFamilyName() { return $this->family_name; } /** * @param mixed $family_name */ public function setFamilyName($family_name) { $this->family_name = $family_name; } /** * @return mixed */ public function getName() { return $this->name; } /** * @param mixed $name */ public function setName($name) { $this->name = $name; } /** * @return mixed */ public function getUniqueName() { return $this->unique_name; } /** * @param mixed $unique_name */ public function setUniqueName($unique_name) { $this->unique_name = $unique_name; } /** * @return mixed */ public function getAppid() { return $this->appid; } /** * @param mixed $appid */ public function setAppid($appid) { $this->appid = $appid; } /** * @return mixed */ public function getAppidacr() { return $this->appidacr; } /** * @param mixed $appidacr */ public function setAppidacr($appidacr) { $this->appidacr = $appidacr; } /** * @return mixed */ public function getScp() { return $this->scp; } /** * @param mixed $scp */ public function setScp($scp) { $this->scp = $scp; } /** * @return mixed */ public function getAcr() { return $this->acr; } /** * @param mixed $acr */ public function setAcr($acr) { $this->acr = $acr; } public function toString(){ return "JWT ==> <br/> aud: ".$this->aud."<br/> iss: ". $this->iss ."<br/> iat: ". $this->iat ."<br/> nbf: ". $this->nbf ."<br/> exp: ". $this->exp ."<br/> ver: ". $this->ver ."<br/> tid: ". $this->tid ."<br/> amr pwd: ". $this->amr->pwd ."<br/> oid: ". $this->oid ."<br/> upn: ". $this->upn ."<br/> puid: ". $this->puid ."<br/> sub: ". $this->sub ."<br/> given_name: ". $this->given_name ."<br/> family_name: ". $this->family_name ."<br/> name: ". $this->name ."<br/> unique_name: ". $this->unique_name ."<br/> appid: ". $this->appid ."<br/> appidacr: ". $this->appidacr ."<br/> scp: ". $this->scp ."<br/> acr: ". $this->acr; } } 

Here is how JWT array really looks like

/******** * Another Example JWT from microsoft site https://msdn.microsoft.com/library/office/dn707383.aspx * * { "aud": "https://manage.office.com", "iss": "https://sts.windows.net/41463f53-8812-40f4-890f-865bf6e35190/", "iat": 1427246416, "nbf": 1427246416, "exp": 1427250316, "ver": "1.0", "tid": "41463f53-8812-40f4-890f-865bf6e35190", "amr": [ "pwd" ], "oid": "1cef1fdb-ff52-48c4-8e4e-dfb5ea83d357", "upn": "[email protected]", "puid": "1003BFFD8EC47CA6", "sub": "7XpD5OWAXM1OWmKiVKh1FOkKXV4N3OSRol6mz1pxxhU", "given_name": "John", "family_name": "Doe", "name": "Contoso, Inc.", "unique_name": "[email protected]", "appid": "a6099727-6b7b-482c-b509-1df309acc563", "appidacr": "1", "scp": "ActivityFeed.Read ServiceHealth.Read", "acr": "1" } * * */ 

HttpPost.php

<?php /** * Created by PhpStorm. * User: msen * Date: 3/10/16 * Time: 12:13 PM */ class HttpPost { public $url; public $postString; public $httpResponse; public $ch; public function __construct($url) { $this->url = $url; $this->ch = curl_init ( $this->url ); curl_setopt ( $this->ch, CURLOPT_FOLLOWLOCATION, false ); curl_setopt ( $this->ch, CURLOPT_HEADER, false ); curl_setopt ( $this->ch, CURLOPT_RETURNTRANSFER, true ); curl_setopt ( $this->ch, CURLOPT_SSL_VERIFYPEER, false ); } public function __destruct() { curl_close ( $this->ch ); } public function setPostData($params) { // http_build_query encodes URLs, which breaks POST data $this->postString = rawurldecode ( http_build_query ( $params ) ); curl_setopt ( $this->ch, CURLOPT_POST, true ); curl_setopt ( $this->ch, CURLOPT_POSTFIELDS, $this->postString ); } public function send() { $this->httpResponse = curl_exec ( $this->ch ); } public function getHttpResponse() { return $this->httpResponse; } } 

Office365_Client.php

<?php /** * Created by PhpStorm. * User: msen * Date: 3/10/16 * Time: 11:58 AM */ require_once "config.php"; require_once "JWT.php"; require_once "HttpPost.php"; class Office365_Client { private $code; private $accessToken; private $refreshToken; private $id_token; private $jwt; public function __construct($config = array()) { global $apiConfig; $apiConfig = array_merge ( $apiConfig, $config ); } public function createAuthUrl() { global $apiConfig; $query_params = array ('response_type' => 'code','client_id' => $apiConfig ['oauth2_client_id'],'client_secret' => $apiConfig ['oauth2_secret'],'redirect_uri' => $apiConfig ['oauth2_redirect'],'resource' => $apiConfig ['resource'],'state' => $apiConfig ['state'] ); $auth_url = $apiConfig ['oauth2_auth_url'] . '?' . http_build_query ( $query_params ); return $auth_url; } public function fetchTokens() { global $apiConfig; $url = $apiConfig['oauth2_token_url']; $params = array ("code" => $this->code,"client_id" => $apiConfig ['oauth2_client_id'],"client_secret" =>$apiConfig ['oauth2_secret'],"resource" => $apiConfig ['resource'],"redirect_uri" => $apiConfig ['oauth2_redirect'],"grant_type" => "authorization_code" ); // build a new HTTP POST request $request = new HttpPost ( $url ); $request->setPostData ( $params ); $request->send(); $responseObj = json_decode($request->getHttpResponse ()); $this->accessToken = $responseObj->access_token; $this->refreshToken = $responseObj->refresh_token; $this->id_token = $responseObj->id_token; } // Fetches JWT returned from Azure to get the user's info public function fetchJWT() { $token_parts = explode(".", $this->getIdToken()); // First part is header, which we ignore // Second part is JWT, which we want to parse // First, in case it is url-encoded, fix the characters to be // valid base64 $encoded_token = str_replace('-', '+', $token_parts[1]); $encoded_token = str_replace('_', '/', $encoded_token); // Next, add padding if it is needed. switch (strlen($encoded_token) % 4){ case 0: // No pad characters needed. break; case 2: $encoded_token = $encoded_token."=="; error_log("Added 2: ".$encoded_token); break; case 3: $encoded_token = $encoded_token."="; error_log("Added 1: ".$encoded_token); break; default: // Invalid base64 string! return null; } $json_string = base64_decode($encoded_token); $jwt_arr = json_decode($json_string, true); $this->jwt = new JWT($jwt_arr); } /** * @return mixed */ public function getCode() { return $this->code; } /** * @param mixed $code */ public function setCode($code) { $this->code = $code; } /** * @return mixed */ public function getAccessToken() { return $this->accessToken; } /** * @param mixed $accessToken */ public function setAccessToken($accessToken) { $this->accessToken = $accessToken; } /** * @return mixed */ public function getRefreshToken() { return $this->refreshToken; } /** * @param mixed $refreshToken */ public function setRefreshToken($refreshToken) { $this->refreshToken = $refreshToken; } /** * @return mixed */ public function getIdToken() { return $this->id_token; } /** * @param mixed $id_token */ public function setIdToken($id_token) { $this->id_token = $id_token; } /** * @return JWT */ public function getJwt() { return $this->jwt; } /** * @param JWT $jwt */ public function setJwt($jwt) { $this->jwt = $jwt; } public function toString(){ return "Office365 ==> <br/> code: ". $this->code ."<br/>". "accessToken: ". $this->accessToken ."<br/>". "refreshToken: ".$this->refreshToken ."<br/>"; } } 

And this is the file you are interacting with Office 365

oauth2.php

<?php /** * Created by PhpStorm. * User: msen * Date: 3/10/16 * Time: 12:25 PM */ require_once('Office365_Client.php'); session_start(); $client = new Office365_Client(); $forward_url = $client->createAuthUrl(); if(isset($_GET['code'])) { //TODO: verfiy unquie key state to check CSRF attack $code = $_GET['code']; $client->setCode($code); //get tokens $client->fetchTokens(); echo '<br/><br/>'; //print access tokens print($client->toString()); echo '<br/><br/>'; //you can set the tokens into your own session $_SESSION['accesstoken'] = $client->getAccessToken(); $_SESSION['refreshtoken'] = $client->getRefreshToken(); //let's get user info $client->fetchJWT(); //print the usr info print($client->getJwt()->toString()); //put the user token info into sessions $_SESSION['name'] = $client->getJwt()->getName();//full name of the user $_SESSION['unique_name'] = $client->getJwt()->getUniqueName();//could be email or id from office365 $_SESSION['tid'] = $client->getJwt()->getTid();//tenant id } else{ //instead of putting a button, you can forward automatically yourself print "<a class='login' href='$forward_url'>Connect Me!</a>"; //you can also redirect automatically //header( 'Location: '.$forward_url ); } ?> 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.