В настоящее время я пытаюсь использовать API-интерфейсы REST Office365, и для этого мне нужно авторизоваться, используя механизм OAuth2. Все идет нормально.
После долгой битвы мне наконец-то удалось получить указанные токены, но мне нужно получить информацию о пользователе (например, его идентификатор, адрес электронной почты, имя …), которая идет с токеном.
Используя песочницу, мне нужно что-то вроде этого:
Я уже пытался получить информацию о пользователе через 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"
Но все, что у меня есть, это следующее:
{"error":{"code":"-2147024891, System.UnauthorizedAccessException","message":"Access denied. You do not have permission to perform this action or access this resource."}}
запрос на получение указанного OAuth-токена основан на следующих параметрах (через 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']);
Итак, есть идеи, как я могу получить информацию о пользователе (даже основные)?
Спасибо
— редактировать
Я думаю, что понял. Казалось бы, с запросом завиток на https://outlook.office365.com/api/v1.0/me
дает упрощенный массив с MailboxGuid, Id, Alias и GivenName:
curl https://outlook.office365.com/api/v1.0/me -X GET -H "Authorization: Bearer <my oauth token>"
Дает следующее (иногда с тайм-аутом, иногда нет … Я думаю, мне нужно поработать над этим, если у кого-то могут быть предложения, не стесняйтесь вмешиваться)
{"@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>"}
Это не так полно, как вещь, возвращаемая песочницей, но она должна дать мне то, что мне нужно …
Но … иногда это дает мне тайм-аут, иногда все идет хорошо … Примерно в соотношении 3/5. Любая идея ? Спасибо
П.С .: Если вам нужно узнать, как я настроил приложение на Azure, спросите
Вы смотрели на идентификационный токен, который возвращается из вашего запроса токена? Это может содержать всю информацию, которую вы хотите, и у вас уже есть, поэтому нет необходимости делать второй запрос.
Вот образец что (помимо прочего) анализирует маркер идентификации, чтобы получить отображаемое имя пользователя. Проверить getUserName
функция в 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'];
}
Посмотрев много источников вокруг .. Я улучшил клиент php office365 здесь
https://github.com/mehmetsen80/office365client
Предположим, у вас есть такой конфигурационный файл:
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',
);
?>
В конце у вас будет такой код ..
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 сообщает нам, какую информацию пользователя можно получить. 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;
}
}
Вот как на самом деле выглядит массив JWT
/********
* 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/>";
}
}
И это файл, с которым вы взаимодействуете с 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 );
}
?>