oops-OAUTH2
[ class tree: oops-OAUTH2 ] [ index: oops-OAUTH2 ] [ all elements ]

Source for file MICROSOFT.php

Documentation is available at MICROSOFT.php

  1. <?php
  2. /**
  3.  * Project: MICROSOFT:: Micosoft (Azure) oauth2 pear package<br>
  4.  * File:    MICROSOFT.php<br>
  5.  * Dependency:
  6.  *   - {@link http://pear.oops.org/docs/li_HTTPRelay.html oops/HTTPRelay}
  7.  *   - {@link http://pear.oops.org/docs/li_myException.html oops/myException}
  8.  *
  9.  * oops/MICROSOFT pear package는 Microsoft(Azuer) Directory oauth2 login을 위한 pear package이다
  10.  *
  11.  * 이 package를 사용하기 위해서는 먼저 Azzer portal 에서 tetant 를 생성한 후
  12.  * app 등록을 해야 한다. (https://portal.azure.com)
  13.  * https://docs.microsoft.com/ko-kr/azure/active-directory/develop/quickstart-register-app
  14.  *
  15.  * @category  HTTP
  16.  * @package   oops\OAUTH2
  17.  * @subpackage   oops\OAUTH2\MICROSOFT
  18.  * @author    JoungKyun.Kim <http://oops.org>
  19.  * @copyright (c) 2019, OOPS.org
  20.  * @license   BSD License
  21.  * @example   OAUTH2/tests/microsoft.php MICROSOFT pear package 예제 코드
  22.  * @since     1.0.9
  23.  * @filesource
  24.  */
  25.  
  26. namespace oops\OAUTH2;
  27.  
  28. /**
  29.  * import HTTPRelay class
  30.  */
  31. require_once 'HTTPRelay.php';
  32.  
  33.  
  34. /**
  35.  * MICROSOFT pear pcakge의 main class
  36.  *
  37.  * OAuth2를 이용하여 MICROSOFT 로그인을 진행하고, 로그인된 사용자의
  38.  * 정보를 얻어온다.
  39.  *
  40.  * @package   oops\OAUTH2
  41.  * @subpackage   oops\OAUTH2\MICROSOFT
  42.  * @author    JoungKyun.Kim <http://oops.org>
  43.  * @copyright (c) 2019, OOPS.org
  44.  * @license   BSD License
  45.  * @since     1.0.9
  46.  * @example   OAUTH2/tests/microsoft.php MICROSOFT pear 예제 코드
  47.  */
  48. Class MICROSOFT {
  49.     // {{{ properities
  50.     /**#@+
  51.      * @access private
  52.      */
  53.     /**
  54.      * 세션 이름
  55.      * @var string 
  56.      */
  57.     private $sessid   '_OAUTH2_';
  58.     /**
  59.      * login url
  60.      * @var string 
  61.      */
  62.     private $reqAuth  'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize';
  63.     /**
  64.      * token url
  65.      * @var string 
  66.      */
  67.     private $reqToken 'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token';
  68.     /**
  69.      * revoke url
  70.      * @var string 
  71.      */
  72.     private $reqRevoke 'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/logout';
  73.     /**
  74.      * user information url
  75.      * @var string 
  76.      */
  77.     private $reqUser  'https://graph.microsoft.com/v1.0/me';
  78.     /**
  79.      * app information
  80.      * @var stdClass memebr는 다음과 같음
  81.      *    - id     : Google login Application ID
  82.      *    - secret : Google login Application Secret key
  83.      *    - callback : 이 class를 호출하는 페이지
  84.      *    - tenant : 로그인 계정의 유형.
  85.      *               'common', 'organizations', 'cunsumers', TENANT ID
  86.      *    - baseurl : reqLogout 호출 후 돌아갈 site call url
  87.      */
  88.     private $apps;
  89.     /**
  90.      * SSL type
  91.      * @var string 
  92.      */
  93.     private $proto 'http';
  94.     /**#@-*/
  95.     /**
  96.      * MICROSOFT 로그인에 필요한 session 값
  97.      * @access public
  98.      * @var stdClass 
  99.      */
  100.     public $sess;
  101.     /**#@-*/
  102.     /**
  103.      * MICROSOFT 로그인 scope
  104.      * @access public
  105.      * @var string 
  106.      */
  107.     public $scope = 'openid offline_access user.read';
  108.     // }}}
  109.  
  110.     // {{{ +-- public (void) __construct ($v)
  111.     /**
  112.      * Google 로그인 인증 과정을 수행한다. 인증 과정 중에
  113.      * 에러가 발생하면 myException 으로 에러 메시지를
  114.      * 보낸다.
  115.      *
  116.      * logout 시에 globale 변수 $_OAUTH2_LOGOUT_TEMPALTE_ 로 사용자 logout template
  117.      * 을 지정할 수 있다. template 파일은 pear/OAUTH2/login-agree.template 를 참조하면 된다.
  118.      *
  119.      * @access public
  120.      * @param stdClass $v 
  121.      *    - id       발급받은 Google login Application ID
  122.      *    - secret   발급받은 Google login Application Scret key
  123.      *               Azuer Active Directory -> App -> 인증서 및 암호의 클아이언트 암호를 설정
  124.      *               하여 사용한다.
  125.      *    - callback 이 클래스가 호출되는 url
  126.      *               callback url 은 Azuer Active Directory -> App -> 인증 -> 리디렉션 URI로
  127.      *               등록 되어 있어야 한다.
  128.      *    - tenant   로그인 계정의 유형을 결정.
  129.      *               https://docs.microsoft.com/ko-kr/azure/active-directory/develop/active-directory-v2-protocols#endpoints 참고
  130.      *    - baseurl  로그아웃 후 돌아갈 callback uri (또는 사이트 root 경로)
  131.      *               baseurl 은 Azuer Active Directory -> App -> 인증 -> 리디렉션 URI로
  132.      *               등록 되어 있어야 한다.
  133.      * @return void 
  134.      */
  135.     function __construct ($v{
  136.         if isset ($_SESSION[$this->sessid]) ) {
  137.             $_SESSION[$this->sessidnew \stdClass;
  138.             $_SESSION[$this->sessid]->appId = (object) $v;
  139.         }
  140.         $this->sess = &$_SESSION[$this->sessid];
  141.         $this->apps = (object) $v;
  142.  
  143.         if isset ($_SERVER['HTTPS']) )
  144.             $this->proto .= 's';
  145.  
  146.         if isset ($_GET['logout']) ) {
  147.             $this->reqLogout ();
  148.             return;
  149.         }
  150.  
  151.         $this->checkError ();
  152.         $this->reqLogin ();
  153.         $this->reqAccessToken ();
  154.     }
  155.     // }}}
  156.  
  157.     // {{{ +-- private (string) mkToken (void)
  158.     /**
  159.      * 세션 유지를 위한 token 값
  160.      *
  161.      * @access private
  162.      * @return string 
  163.      */
  164.     private function mkToken ({
  165.         $mt microtime ();
  166.         $rand mt_rand ();
  167.         return md5 ($mt $rand);
  168.     }
  169.     // }}}
  170.  
  171.     // {{{ +-- private (void) reqLogin (void)
  172.     /**
  173.      * 로그인 창으로 redirect
  174.      *
  175.      * @access private
  176.      * @return void 
  177.      */
  178.     private function reqLogin ({
  179.         $app &$this->apps;
  180.         $this->sess->state $this->mkToken ();
  181.  
  182.         if $_GET['code'|| isset ($this->sess->oauth)  )
  183.             return;
  184.  
  185.         $this->reqAuth preg_replace ('/{tenant}/'$app->tenant$this->reqAuth);
  186.  
  187.         # '%s?client_id=%s&response_type=code&redirect_uri=%s&' .
  188.         # 'response_mode=from_post&scope=openid&state=%s&nonce=%s',
  189.         #  $this->reqAuth, $app->id, rawurlencode ($app->callback), $this->sess->state
  190.         $url sprintf (
  191.             '%s?client_id=%s&response_type=code&redirect_uri=%s&' .
  192.             'response_mode=query&scope=%s&state=%s',
  193.             $this->reqAuth$app->idrawurlencode ($app->callback),
  194.             $this->scope,
  195.             $this->sess->state
  196.         );
  197.  
  198.         Header ('Location: ' $url);
  199.         exit;
  200.     }
  201.     // }}}
  202.  
  203.     // {{{ +-- private (void) reqAccessToken (void)
  204.     /**
  205.      * Authorization code를 발급받아 session에 등록
  206.      *
  207.      * MICROSOFT::$sess->oauth 를 stdClass로 생성하고 다음의
  208.      * member를 등록한다.
  209.      *
  210.      *   - access_token:      발급받은 access token. expires_in(초) 이후 만료
  211.      *   - refresh_token:     access token 만료시 재발급 키 (14일 expire)
  212.      *   - token_type:        Bearer or MAC
  213.      *   - expires_in:        access token 유효시간(초)
  214.      *   - error:             error code
  215.      *   - error_description: error 상세값
  216.      *
  217.      * 등록된 권한 동의는 https://microsoft.com/consent 에서 철회가 가능하다
  218.      *
  219.      * @access private
  220.      * @return void 
  221.      */
  222.     private function reqAccessToken ({
  223.         $sess &$this->sess;
  224.         $app  &$this->apps;
  225.  
  226.         if $_GET['code'|| isset ($sess->oauth) )
  227.             return;
  228.  
  229.         $this->reqToken preg_replace ('/{tenant}/'$app->tenant$this->reqToken);
  230.  
  231.         $post array (
  232.             'client_id' => $app->id,
  233.             'scope' => $this->scope,
  234.             'code' => $_GET['code'],
  235.             'redirect_uri' => $app->callback,
  236.             'grant_type' => 'authorization_code',
  237.             'client_secret' => $app->secret
  238.         );
  239.  
  240.         $http new \HTTPRelay;
  241.         $buf $http->fetch ($this->reqToken10''$post);
  242.  
  243.         #if ( $http->info['http_code'] != 200 )
  244.         if $buf === false )
  245.             $this->error ($http->error);
  246.  
  247.         $r json_decode ($buf);
  248.  
  249.         if $r->error )
  250.             $this->error ($r->error_description);
  251.         
  252.         $sess->oauth = (object) $r;
  253.     }
  254.     // }}}
  255.  
  256.     // {{{ +-- private (void) checkError (void)
  257.     /**
  258.      * 에러 코드가 존재하면 에러 처리를 한다.
  259.      *
  260.      * @access private
  261.      * @return void 
  262.      */
  263.     private function checkError ({
  264.         $sess &$this->sess;
  265.  
  266.         if $_POST['error')
  267.             $this->error ($_PSOT['error_description']);
  268.  
  269.         # 복원
  270.         #if ( $_POST['state'] && $_POST['state'] != $sess->state )
  271.         #    $this->error ('Invalude Session state: ' . $_POST['state']);
  272.     }
  273.     // }}}
  274.  
  275.     // {{{ +-- private (void) error ($msg)
  276.     /**
  277.      * 에러를 Exception 처리한다.
  278.      *
  279.      * @access private
  280.      * @return void 
  281.      */
  282.     private function error ($msg{
  283.         $msg $_SERVER['HTTP_REFERER'"\n" $msg;
  284.         throw new \myException ($msgE_USER_ERROR);
  285.     }
  286.     // }}}
  287.  
  288.     // {{{ +-- private (string) redirectSelf (void)
  289.     /**
  290.      * 현재 URL에 after argument를 set한다.
  291.      *
  292.      * @access private
  293.      * @return string 
  294.      */
  295.     private function redirectSelf ({
  296.         if trim ($_SERVER['QUERY_STRING']) )
  297.             $qs sprintf ('?%s&after'$_SERVER['QUERY_STRING']);
  298.         else
  299.             $qs '?after';
  300.  
  301.         $req preg_replace ('/\?.*/'''trim ($_SERVER['REQUEST_URI']));
  302.         if $req $req '/';
  303.  
  304.         return sprintf (
  305.             '%s://%s%s%s',
  306.             $this->proto$_SERVER['HTTP_HOST']$req$qs
  307.         );
  308.     }
  309.     // }}}
  310.  
  311.     // {{{ +-- public (stdClass) Profile (void)
  312.     /**
  313.      * 로그인 과정이 완료되면 발급받은 oops\OAUTH2\MICROSOFT::$sess->oauth 에
  314.      * 등록된 키를 이용하여 로그인 사용자의 정보를 가져온다.
  315.      *
  316.      * @access public
  317.      * @return stdClass 다음의 object를 반환
  318.      *    - id     사용자 UID
  319.      *    - name   사용자 별칭
  320.      *    - email  이메일
  321.      *    - img    프로필 사진 URL 정보 (Don't support. Not yet support for personal account)
  322.      *    - r      MS profile 원본 값
  323.      */
  324.     public function Profile ({
  325.         $sess &$this->sess;
  326.  
  327.         if isset ($sess->oauth) )
  328.             return false;
  329.  
  330.         $req $sess->oauth->token_type ' ' $sess->oauth->access_token;
  331.  
  332.         $header array ('Authorization' => $req);
  333.         $http new \HTTPRelay ($header);
  334.         $buf $http->fetch ($this->reqUser);
  335.  
  336.         if $buf )
  337.             $this->error (sprintf ('[OAUTH2] Failed get user profile for %s'__CLASS__));
  338.  
  339.         $r json_decode ($buf);
  340.  
  341.         $re array (
  342.             'id' => $r->id,
  343.             'name' => $r->displayName,
  344.             'email' => $r->mail $r->mail $r->userPrincipalName,
  345.             'img'  => $r->picture,
  346.             'r' => $r
  347.         );
  348.  
  349.         /*
  350.          * Not yet support for personal account
  351.          * https://docs.microsoft.com/ko-kr/graph/api/profilephoto-get?view=graph-rest-1.0
  352.         unset ($buf);
  353.         $buf = $http->fetch ('https://graph.microsoft.com/v1.0/me/photo');
  354.         echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
  355.         print_r ($http);
  356.         print_r ($buf);
  357.         echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
  358.          */
  359.  
  360.         return (object) $re;
  361.     }
  362.     // }}}
  363.  
  364.     // {{{ +-- public (void) reqLogout (void)
  365.     /**
  366.      * Microsoft 로그인의 authorization key를 만료 시키고
  367.      * 세션에 등록된 정보(oops\OAUT2\MICROSOFT::$sess)를 제거한다.
  368.      *
  369.      * 로그 아웃 후, MICROSOFT::$app->baseurl 로 이동을 한다.
  370.      *
  371.      * @access public
  372.      * @return void 
  373.      */
  374.     public function reqLogout ({
  375.         $sess &$this->sess;
  376.         $app  &$this->apps;
  377.  
  378.         if isset ($sess->oauth) )
  379.             return;
  380.  
  381.         if isset ($_GET['after']) ) {
  382.             unset ($_SESSION[$this->sessid]);
  383.             $this->reqRevokepreg_replace ('/{tenant}/'$app->tenant$this->reqRevoke);
  384.  
  385.             $redirect sprintf (
  386.                 '%s?post_logout_redirect_uri=%s',
  387.                 $this->reqRevokerawurlencode ($app->baseurl)
  388.             );
  389.             Header ('Location: ' $redirect);
  390.             exit;
  391.         }
  392.  
  393.         unset ($_SESSION[$this->sessid]);
  394.     }
  395.     // }}}
  396. }
  397.  
  398. /*
  399.  * Local variables:
  400.  * tab-width: 4
  401.  * c-basic-offset: 4
  402.  * End:
  403.  * vim600: noet sw=4 ts=4 fdm=marker
  404.  * vim<600: noet sw=4 ts=4
  405.  */
  406. ?>

Documentation generated on Tue, 14 May 2019 02:00:32 +0900 by phpDocumentor 1.4.4