Wednesday, October 19, 2016

Tutorial OAuth2 dengan CodeIgniter

3 comments
Pada kesempatan kali ini, saya akan membahas cara menggunakan OAuth (Open standard for Authentification) versi 2 pada framework CodeIgniter. Sebelumnya apa itu OAuth2? OAuth2 merupakan pengembangan dari OAuth versi pertama, yaitu sebuah protokol atau framework untuk mengamankan resource (biasanya webservice) dengan mengotorisasi penggunanya sebelum dapat menggunakan resource tersebut.

Persiapan :

Langkah-Langkah :

Ekstrak CodeIgniter 3.1.0.zip, letakkan di document root web server anda, rename foldernya menjadi ci-oauth2

Ekstrak library OAuth2 Server CodeIgniter dan rename nama foldernya menjadi oauth2, letakkan folder tersebut pada direktori library CodeIgniter (application/libraries/)

Kita akan melakukan instalasi oauth2-server-php dengan composer, buka terminal / console, ubah directory ke application/libararies/oauth2, ketikkan :
composer install
Install OAuth2 Server PHP via Composer
Bagi yang kesulitan menginstall via composer,  saya memberikan link lengkap source code di bagian akhir artikel ini.

Buat library CodeIgniter dengan nama Server.php (application/libraries/Server.php), ketikkan listing perintah berikut:
<?php
/**
* @package     Server.php
* @author      Aditya Nursyahbani
* @link        http://www.aditya-nursyahbani.net/2016/10/tutorial-oauth2-dengan-codeigniter.html
* @copyright   Copyright(c) 2016
* @version     1.0.0
**/
date_default_timezone_set('Asia/Jakarta');

class Server{
 function __construct($config=array()){
  require_once(__DIR__.'/../config/database.php');       
  require_once(__DIR__.'/../libraries/oauth2/src/OAuth2/Autoloader.php'); 
  $config = $db['oauth'];
  
  OAuth2Autoloader::register();
  $this->storage = new OAuth2StoragePdo(array('dsn' => $config["dsn"], 'username' => $config["username"], 'password' => $config["password"]));
  $this->server = new OAuth2Server($this->storage, array('allow_implicit' => true));
  $this->request = OAuth2Request::createFromGlobals();
  $this->response = new OAuth2Response();
 }

 /**
 * client_credentials
 */
 public function client_credentials(){
  $this->server->addGrantType(new OAuth2GrantTypeClientCredentials($this->storage, array(
      "allow_credentials_in_request_body" => true
  )));
  $this->server->handleTokenRequest($this->request)->send();
 }

 /**
 * password_credentials
 */
 public function password_credentials(){
  $users = array("adit" => array("password" => 'pass', 'first_name' => 'homeway', 'last_name' => 'yao'));
  $storage = new OAuth2StorageMemory(array('user_credentials' => $users));
  $this->server->addGrantType(new OAuth2GrantTypeUserCredentials($storage));
  $this->server->handleTokenRequest($this->request)->send();
 }

 /**
 * refresh_token
 */
 public function refresh_token(){
  $this->server->addGrantType(new OAuth2GrantTypeRefreshToken($this->storage, array(
   "always_issue_new_refresh_token" => true,
   "unset_refresh_token_after_use" => true,
   "refresh_token_lifetime" => 2419200,
  )));
  $this->server->handleTokenRequest($this->request)->send();
 }

 /**
 * limit scope
 */
 public function require_scope($scope=""){
  if (!$this->server->verifyResourceRequest($this->request, $this->response, $scope)) {
      $this->server->getResponse()->send();
      die;
  }
 }

 public function check_client_id(){
  if (!$this->server->validateAuthorizeRequest($this->request, $this->response)) {
      $this->response->send();
      die;
  }
 }

 public function authorize($is_authorized){
  $this->server->addGrantType(new OAuth2GrantTypeAuthorizationCode($this->storage));
  $this->server->handleAuthorizeRequest($this->request, $this->response, $is_authorized);
  if ($is_authorized) {
     $code = substr($this->response->getHttpHeader('Location'), strpos($this->response->getHttpHeader('Location'), 'code=')+5, 40);
     header("Location: ".$this->response->getHttpHeader('Location'));
    }
  $this->response->send();
 }

 public function authorization_code(){
  $this->server->addGrantType(new OAuth2GrantTypeAuthorizationCode($this->storage));
  $this->server->handleTokenRequest($this->request)->send();
 }
}
 /**
 * password_credentials
 */
 public function password_credentials(){
  $users = array("user" => array("password" => 'pass', 'first_name' => 'aditya', 'last_name' => 'nursyahbani'));
  $storage = new OAuth2\Storage\Memory(array('user_credentials' => $users));
  $this->server->addGrantType(new OAuth2\GrantType\UserCredentials($storage));
  $this->server->handleTokenRequest($this->request)->send();
 }

 /**
 * refresh_token
 */
 public function refresh_token(){
  $this->server->addGrantType(new OAuth2\GrantType\RefreshToken($this->storage, array(
   "always_issue_new_refresh_token" => true,
   "unset_refresh_token_after_use" => true,
   "refresh_token_lifetime" => 2419200,
  )));
  $this->server->handleTokenRequest($this->request)->send();
 }

 /**
 * limit scope
 */
 public function require_scope($scope=""){
  if (!$this->server->verifyResourceRequest($this->request, $this->response, $scope)) {
      $this->server->getResponse()->send();
      die;
  }
 }

 public function check_client_id(){
  if (!$this->server->validateAuthorizeRequest($this->request, $this->response)) {
      $this->response->send();
      die;
  }
 }

 public function authorize($is_authorized){
  $this->server->addGrantType(new OAuth2\GrantType\AuthorizationCode($this->storage));
  $this->server->handleAuthorizeRequest($this->request, $this->response, $is_authorized);
  if ($is_authorized) {
     $code = substr($this->response->getHttpHeader('Location'), strpos($this->response->getHttpHeader('Location'), 'code=')+5, 40);
     header("Location: ".$this->response->getHttpHeader('Location'));
    }
  $this->response->send();
 }

 public function authorization_code(){
  $this->server->addGrantType(new OAuth2\GrantType\AuthorizationCode($this->storage));
  $this->server->handleTokenRequest($this->request)->send();
 }
}

Ekstrak file Codeigniter Rest Client sesuai dengan direktorinya masing pada folder ci-oauth2

Pada artikel ini menggunakan database mysql, buat tabel database dengan nama oauth2, buka phpmyadmin/consol dan import file oauth2.sql dan country.sql ke tabel oauth2.

Konfigurasi koneksi database dengan codeigniter, buka file database.php yang terletak pada direktori application/config. Tambahkan baris berikut:

$db['oauth'] = array(
 'dsn' => 'mysql:dbname=oauth2;host=127.0.0.1',
 'hostname' => 'localhost',
 'username' => 'USERNAME_DB_HERE',
 'password' => 'PASSWORD_DB_HERE',
 'database' => 'oauth2',
 'dbdriver' => 'pdo',
 'dbprefix' => '',
 'pconnect' => FALSE,
 'db_debug' => (ENVIRONMENT !== 'production'),
 'cache_on' => FALSE,
 'cachedir' => '',
 'char_set' => 'utf8',
 'dbcollat' => 'utf8_general_ci',
 'swap_pre' => '',
 'encrypt' => FALSE,
 'compress' => FALSE,
 'stricton' => FALSE,
 'failover' => array(),
 'save_queries' => TRUE
);
Buat direktori dengan nama Oauth2 pada direktori application/controllers, buat controller-controller berikut:
Authorize.php
<?php
/**
* @package     Authorize.php
* @author      Aditya Nursyahbani
* @link        http://www.aditya-nursyahbani.net/2016/10/tutorial-oauth2-dengan-codeigniter.html
* @copyright   Copyright(c) 2016
* @version     1.0.0
**/

defined('BASEPATH') OR exit('No direct script access allowed');

class Authorize extends CI_Controller {
    function __construct(){
        @session_start();
        parent::__construct();
        $this->load->library("Server", "server");
    }

    function index(){
        $scope = $this->input->get("scope");
        $state = $this->input->get("state");
        $client_id = $this->input->get("client_id");
        $redirect_uri = $this->input->get("redirect_uri");
        $response_type = $this->input->get("response_type");

        if(!isset($_POST["authorized"])){
            $this->server->check_client_id();
            $data = array(
                "scope" => $scope,
                "state" => $state,
                "client_id" => $client_id,
                "redirect_uri" => $redirect_uri,
                "response_type" => $response_type,
            );
            $this->load->view("oauth2/authorize", $data);
        }else{
            $authorized = $this->input->post("authorized");
            if($authorized === "yes"){
                $this->server->authorize(($authorized === "yes"));
            }else{
                echo "error";
            }
        }
    }

    function token(){
        $this->server->authorization_code("yes");
    }
}

ClientCredentials.php
<?php
/**
* @package     ClientCredentials.php
* @author      Aditya Nursyahbani
* @link        http://www.aditya-nursyahbani.net/2016/10/tutorial-oauth2-dengan-codeigniter.html
* @copyright   Copyright(c) 2016
* @version     1.0.0
**/

defined('BASEPATH') OR exit('No direct script access allowed');

class ClientCredentials extends CI_Controller {
    function __construct(){
        @session_start();
        parent::__construct();
        $this->load->library("Server", "server");
    }    

    function index(){
        $this->server->client_credentials();
    }
}

PasswordCredentials.php
<?php
/**
* @package     PasswordCredentials.php
* @author      Aditya Nursyahbani
* @link        http://www.aditya-nursyahbani.net/2016/10/tutorial-oauth2-dengan-codeigniter.html
* @copyright   Copyright(c) 2016
* @version     1.0.0
**/
defined('BASEPATH') OR exit('No direct script access allowed');

class PasswordCredentials extends CI_Controller {
    function __construct(){
        @session_start();
        parent::__construct();
        $this->load->library("Server", "server");
    }    

    function index(){
        $this->server->password_credentials();
    }
}

RefreshToken.php
<?php
/**
* @package     RefreshToken.php
* @author      Aditya Nursyahbani
* @link        http://www.aditya-nursyahbani.net/2016/10/tutorial-oauth2-dengan-codeigniter.html
* @copyright   Copyright(c) 2016
* @version     1.0.0
**/
defined('BASEPATH') OR exit('No direct script access allowed');

class RefreshToken extends CI_Controller {
    function __construct(){
        @session_start();
        parent::__construct();
        $this->load->library("Server", "server");
    }    

    function index(){
        $this->server->refresh_token();
    }
}

Test.php
<?php
/**
* @package     Test.php
* @author      Aditya Nursyahbani
* @link        http://www.aditya-nursyahbani.net/2016/10/tutorial-oauth2-dengan-codeigniter.html
* @copyright   Copyright(c) 2016
* @version     1.0.0
**/

defined('BASEPATH') OR exit('No direct script access allowed');

class Test extends CI_Controller {
    function __construct(){
        @session_start();
        parent::__construct();
    }

    function index(){
     $this->load->view("oauth2/api");
    }

}

Buatlah direktori dengan nama oauth2 pada direktori application/views kemudian buatlah view berikut didalam direktori oauth2 :
api.php
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>CodeIgniter OAuth2 Tutorial</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta name="description" content="CodeIgniter OAuth2 Tutorial">
    <meta property="og:type" content="website">
    <meta property="og:title" content="CodeIgniter OAuth2 Tutorial">
    <meta property="og:url" content="http://homeway.me">
    <meta property="og:site_name" content="CodeIgniter OAuth2 Tutorial">
    <meta property="og:description" content="CodeIgniter OAuth2 Tutorial">
    <meta name="twitter:title" content="@adit_gudhel">
    <link rel="publisher" href="aditya nursyahbani">
    <link href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="row">
    <div class="col-md-2"></div>
    <div class="col-md-8">
        <br><br>
        <form class="form-inline">
            <h1>1. Request Access Token (Grant Type)</h1><br><br>

            <div class="form-group">
                <label for="Credentials"><h3>Resource Owner Password Credentials:</h3><br>
                    <div>Param:<code>{ grant_type: "password", username: "adit", password: "pass", client_id: 'client', client_secret:'mysecret', scope:'country' }</code></div><br>
                    <div>Result:<code id="return-PasswordCredentials"></code></div><br>
                </label><br>
                <a id="btn-PasswordCredentials" class="btn btn-success">Get PasswordCredentials</a><br>
            </div>
            <br><br>

            <div class="form-group">
                <label for="ClientCredentials"><h3>Client Credentials: </h3><br>
                    <div>Param:<code>{client_id: 'client', client_secret:'mysecret', grant_type:'client_credentials', scope:'country'}</code></div><br>
                    <div>Result:<code id="return-ClientCredentials"></code></div><br>
                </label><br>
                <a id="btn-ClientCredentials" class="btn btn-success">Get ClientCredentials</a>
            </div>
            <br><br>

            <div class="form-group">
                <label for="authorize">
                    <h3>Access_token(Authorization Code):</h3>
                    <div>Param:<code>{code: "Authorize code", client_id: 'client', client_secret:'mysecret', password:'pass', grant_type: 'authorization_code', scope:'country'}</code></div><br>
                    <div>Result:<code id="return-Authorize"></code></div><br>
                    <div>Link Auth <a target="_blank" href="/ci-oauth2/oauth2/authorize?response_type=code&client_id=client&redirect_uri=http://www.aditya-nursyahbani.net&state=xyz&scope=country">/ci-oauth2/oauth2/authorize?response_type=code&client_id=client&redirect_uri=<?=base_url('oauth2/test');?>&state=xxx&scope=country</a></div><br>
                </label><br>
                <input type="text" class="form-control" id="authorize-code" placeholder="Authorize code">
                <a id="btn-authorize" class="btn btn-success">Get Authorize Access Token</a>
            </div>

            <br><br>
            <div class="form-group">
                <label for="authorize">
                    <h3>Implicit Grant:</h3><br>
                    <div>Authorization Code Access Token</div><br>
                </label>
            </div>

            <br><br><br>
            <h1>2. Access Token </h1>
            <br><br><br>

            <div class="form-group">
                <label for="refresh-token">
                    <h3>Refresh Access Token</h3><br>
                    <div>Param:<code>{refresh_token: "Refresh Token", client_id: 'client', client_secret:'mysecret', grant_type:'refresh_token', scope:'country'}</code></div>
                    <div>Result: <code id="return-refresh-token"></code></div><br>
                </label><br>
                <input type="text" class="form-control refresh-token" id="old_token" placeholder="Refresh Token">
                <a id="btn-refresh" class="btn btn-success">From Refresh Token Get Access Token</a>
            </div>
            <br><br><br><br>

            <div class="form-group">
                <label for="resource">
                    <h3>From Access Token get Resource</h3><br>
                    <div>Param:<code>{ access_token: "Access Token"}</code></div><br>
                    <div>Result: <code id="return-resource"></code></div><br>
                </label><br>
                <input type="text" class="form-control token" id="resource" placeholder="Resource">
                <a id="btn-resource" class="btn btn-success">Get Resource</a>
                <a id="btn-resource-limit" class="btn btn-success">Get Resource With Scope Error</a>
            </div>
            <br><br>
        </form>
    </div>
</div>

<script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script>
<script>
$(document).ready(function(){
    $("#btn-refresh").click(function (e){
        var data = {refresh_token: $("#old_token").val(), client_id: 'client', client_secret:'mysecret', grant_type:'refresh_token', scope:'country'};
        
        $.post('/ci-oauth2/oauth2/RefreshToken', data, function (d){
            console.log("Get Token => ", d);
            $('#return-refresh-token').html(JSON.stringify(d));
        });
    }); 

    $('#btn-authorize').click(function (e){
        var data = {code: $("#authorize-code").val(), client_id: 'client', client_secret:'mysecret', redirect_uri:"http://www.aditya-nursyahbani.net", grant_type: 'authorization_code', scope:'country'};
        $.post('/ci-oauth2/oauth2/authorize/token', data, function (d){
            $(".refresh-token").val(d.refresh_token);
            console.log("Get Authorize Access Token => ", d);
            $('#return-Authorize').html(JSON.stringify(d));
        },"json");
    });

    $('#btn-resource').click(function (e){
        var data = { access_token: $("#resource").val()};
        
        $.post('/ci-oauth2/api/countries', data, function (d){
            console.log("Get Authorize => ", d);
            $('#return-resource').html(JSON.stringify(d));
        },"json");      
    });

    $('#btn-resource-limit').click(function (e){
        var data = { access_token: $("#resource").val()};
        
        $.post('/ci-oauth2/oauth2/resource/limit', data, function (d){
            console.log("Get Authorize => ", d);
            $('#return-resource').html(JSON.stringify(d));
        },"json");      
    });

    $('#btn-PasswordCredentials').click(function (e){
        var data = { grant_type: "password", username: "adit", password: "pass", client_id: 'client', client_secret:'mysecret', scope:'country' };
        $.post('/ci-oauth2/oauth2/PasswordCredentials', data, function (d){
            console.log("Get Access Token from client credentials => ", d);
            $(".refresh-token").val(d.refresh_token);
            $(".token").val(d.access_token);
            $('#return-PasswordCredentials').html(JSON.stringify(d));
        },"json");
    });

    $("#btn-ClientCredentials").click(function (e){
        var data = {client_id: 'client', client_secret:'mysecret', grant_type:'client_credentials', scope:'country'};
        $.post('/ci-oauth2/oauth2/ClientCredentials', data, function (d){
            //d = d.JSON.parse(d);
            console.log("Get Access Token from password credentials => ", d);
            $(".token").val(d.access_token);
            $('#return-ClientCredentials').html(JSON.stringify(d));
        }, "json");
    });
});
</script>

authorize.php
<?php
$action = "/ci-oauth2/oauth2/Authorize?response_type=".$response_type."&client_id=".$client_id."&redirect_uri=".$redirect_uri."&state=".$state."&scope=".$scope;
?>

<body>
<div style="text-align:center;">
  <form method="post">
       <div class="oauth_content" node-type="commonlogin">
         <p class="oauth_main_info">Authorisasi?  <a href="http://www.aditya-nursyahbani.net/"  target="_blank" class="app_name">Ditya's Life</a></p>
   <input name="authorized" value="yes" hidden>
   <button>submit</button>
  </div>
    </form>
  
    </div>
</body>
</html>

Finally buatlah controller dengan nama Api.php fungsinya untuk membuat service-service apa saja yg mau diprovide / disediakan oleh web service, pada artikel ini kita mengambil contoh listing nama-nama negara. Ketikkan listing program berikut:

<?php
/**
* @package     Api.php
* @author      Aditya Nursyahbani
* @link        http://www.aditya-nursyahbani.net/2016/10/tutorial-oauth2-dengan-codeigniter.html
* @copyright   Copyright(c) 2016
* @version     1.0.0
**/

defined('BASEPATH') OR exit('No direct script access allowed');

require(APPPATH . 'libraries/REST_Controller.php');
 
class Api extends REST_Controller {

 function __construct(){
        @session_start();
        parent::__construct();
        $this->load->library("Server", "server");
     $this->server->require_scope("country");

        $this->load->database('oauth');
     $this->load->model('country_model');
    }

 function country_get(){
  if(!$this->get('id')) {
            $this->response(NULL, 400);
        }
 
        $country = $this->country_model->get($this->get('id'));
         
        if($country) {
            $this->response($country, 200); // 200 being the HTTP response code
        } else {
            $this->response(NULL, 404);
        }
 } 

 function countries_post(){
  $country = $this->country_model->get_all();
         
        if($country) {
            $this->response($country, 200); // 200 being the HTTP response code
        } else {
            $this->response(NULL, 404);
        }
 } 

}

Untuk mencoba oauth2nya ketikkan alamat http://localhost/ci-oauth2/oauth2/test


Download full source code pada artikel ini :

3 comments:

  1. [HELP]
    Jika ada error ini bagaimana ya solusinya :

    Fatal error: Uncaught exception 'InvalidArgumentException' with message 'configuration array must contain "dsn"' in /home/dev/t2g_api/app/third_party/Oauth2/src/OAuth2/Storage/Pdo.php:45
    Stack trace:
    #0 /home/dev/t2g_api/app/libraries/Server.php(25): OAuth2\Storage\Pdo->__construct(Array)
    #1 /home/dev/t2g_api/sys/core/Loader.php(1099): Server->__construct()
    #2 /home/dev/t2g_api/sys/core/Loader.php(975): CI_Loader->_ci_init_class('Server', '', NULL, NULL)
    #3 /home/dev/t2g_api/sys/core/Loader.php(216): CI_Loader->_ci_load_class('Server', NULL, NULL)
    #4 /home/dev/t2g_api/app/controllers/Oauth2/Authorize.php(16): CI_Loader->library('Server', 'server')
    #5 /home/dev/t2g_api/sys/core/CodeIgniter.php(308): Authorize->__construct()
    #6 /home/dev/t2g_api/index.php(23): require_once('/home/dev/t2g_a...')
    #7 {main}
    thrown in /home/dev/t2g_api/app/third_party/Oauth2/src/OAuth2/Storage/Pdo.php on line 45

    ReplyDelete
    Replies
    1. Coba di konfigurasi databasenya menggunakan db_driver pdo (PHP Data Object) jangan lupa diaktifkan dulu ektensinya pdo.

      Delete
  2. gan saya sudah punya tabel user sendiri nih,
    gimana caranya ya biar tabel oauth_users diganti menjadi tabel yang sudah saya buat sebelumnya tsb

    ReplyDelete