使用来自office365 oauth api的oauth令牌检索用户信息

时间:2015-02-26 11:29:59

标签: php api azure oauth-2.0 office365

我目前正在尝试使用Office365 REST API,为此,我需要使用OAuth2机制进行身份验证。到目前为止,非常好。

经过长时间的战斗,我终于设法获取了所述令牌,但我需要获取与令牌一起使用的用户信息(例如他的标识符,电子邮件,姓名等)。

使用沙箱,我需要这样的东西:

enter image description here

我已经尝试通过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上的curl请求给出了一个带有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('baptiste@wisembly.onmicrosoft.com')",
 "Id":"baptiste@wisembly.onmicrosoft.com",
 "DisplayName":"Baptiste Clavi\u00e9",
 "Alias":"baptiste",
 "MailboxGuid":"<snip>"}

它不像沙盒返回的东西那么完整,但它应该给我我需要的东西......

但是...它有时会给我一个暂停,有时它会很好......大约是3/5的比例。任何的想法 ?感谢

P.S:如果您需要知道我如何在azure上配置应用程序,请询问

2 个答案:

答案 0 :(得分:2)

您是否查看了令牌请求中返回的身份令牌?这可能包含您想要的所有信息,并且您已经拥有它,因此无需再发出第二个请求。

这里有一个sample(除其他外)解析身份令牌以获取用户的显示名称。查看Office365Service.php中的getUserName功能:

// 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'];
}

答案 1 :(得分:0)

通过查看许多来源......我在这里改进了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": "admin@contoso.onmicrosoft.com",
    "puid": "1003BFFD8EC47CA6",
    "sub": "7XpD5OWAXM1OWmKiVKh1FOkKXV4N3OSRol6mz1pxxhU",
    "given_name": "John",
    "family_name": "Doe",
    "name": "Contoso, Inc.",
    "unique_name": "admin@contoso.onmicrosoft.com",
    "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 );
}

?>