Implementing authentication with tokens for RESTful applications using yii2

REST is that its stateless protocol used for the api. This means that the server never keeps user state for this aspect we need to implement security for data exchanging between api's.We need to add authentication must be sent and verified at each time. There are many ways to secure your api to handle the authentication , the HTTP basic authentication and OAuth2 are default provided by the yii2.




Yii2 Authentication component are following:


HTTP Basic Auth: We need to send the access token as the username. This should only be used when an access token can be safely stored on the API consumer side. For example, the API consumer is a program running on a server.

Query parameter: In this we need to send access token as a query parameter in the API URL, e.g., https://yiiapp.local/users?access-token=xxxxxxxx. Because most Web servers will keep query parameters in server logs, this approach should be mainly used to serve JSONP requests which cannot use HTTP headers to send access tokens.

OAuth 2: The access token is obtained by the consumer from an authorization server and sent to the API server via HTTP Bearer Tokens, according to the OAuth2 protocol.


use yii\filters\auth\CompositeAuth;
use yii\filters\auth\HttpBasicAuth;
use yii\filters\auth\HttpBearerAuth;
use yii\filters\auth\QueryParamAuth;

public function behaviors()
{
    $behaviors = parent::behaviors();
    $behaviors['authenticator'] = [
        'class' => CompositeAuth::className(),
        'authMethods' => [
            HttpBasicAuth::className(),
            HttpBearerAuth::className(),
            QueryParamAuth::className(),
        ],
    ];
    return $behaviors;
}


Authentication in Yii2 : click here

I'm showing you how to use a custom authentication token for security of data exchange. You need to create a super controller (base) and it should be extended to all your child controller for the use of its common functions for resonse array and header genreate also you can write every api log into mongo db or other database using the base controller.


Now i'm using some algorthim for making the hash .I’m using the HMAC-SHA authentication implementation package for PHP. This package will allow you to create requests and hash them in your client code, and then authenticate the request on the API side.


1. You need to send the params input of user sending through the api like.

// Sending Params
$params = array('name' => 'Mark', 'email' => 'markg@gmail.com');

2. For Authenticate with the API, we have a key and a secret that only us and the API server know about. We can use this key and secret combination to create a Token. The api token will be shared to client who will be access the api.

Here i'm defining the apitoken key in the params common/config/params.php


return [
    'ApiTokenKey'=>'fa469e393f632'
];

3. You have to make a hash with the sending query params and the api token key which you  have stored locally. Make a hash with using this algorithim.

$encodeData = json_encode($params);
$hash = hash_hmac('sha256', $encodeData, Yii::$app->params['ApiTokenKey']);

it will create a hash something like this:
2b377a8221f13a86ad0acedd2fsd22474ea5f5e22dc6afcf06cf6b

4. Then send the hash in header with key X-API-TOKEN (you have to rename the key and change in your base controller for use).

5. When you send the request with the api header token the server file will use the same algorithim and  create a token in server end and verify both token will be same. If token will not same then return false with the error like:


{
  "status": true,
  "data": {
    "response": false,
    "message": "Invalid token credentials"
  }
}

Please find the below code for using these algorthim in your end of controller files.

use yii\rest\Controller;
/**
 * @author     SJ <imsonujangra@gmail.com>
 * @package    Base Controller API
 * @created    30-Aug-2016
 * @version    1.0
 */
class ApiBaseController extends Controller
{

    public function response($code, $data = '')
    {
        $response = array();
        $message = $this->getStatusCodeMessage($code);
        if (!empty($message)) {
            //$response = array("status" => false, "message" => $message, "data" => $data, "code" => $code);
            $response = array("status" => true, "data" => $data);
        }
        $this->setHeader($code);

        echo json_encode($response);die;
    }

    private function getStatusCodeMessage($status)
    {
        $codes = Array(
            // Success 2xx
            200 => 'OK',
            201 => 'Created',
            202 => 'Accepted',
            203 => 'Non-Authoritative Information',
            204 => 'No Content',
            205 => 'Reset Content',
            206 => 'Partial Content',
            // Client Error 4xx
            400 => 'Bad Request',
            401 => 'Unauthorized',
            402 => 'Payment Required',
            403 => 'Forbidden',
            404 => 'Not Found',
            405 => 'Method Not Allowed',
            406 => 'Not Acceptable',
            407 => 'Proxy Authentication Required',
            408 => 'Request Timeout',
            409 => 'Conflict',
            410 => 'Gone',
            411 => 'Length Required',
            412 => 'Precondition Failed',
            413 => 'Request Entity Too Large',
            414 => 'Request-URI Too Long',
            415 => 'Unsupported Media Type',
            416 => 'Requested Range Not Satisfiable',
            417 => 'Expectation Failed',
            // Server Error 5xx
            500 => 'Internal Server Error',
            501 => 'Not Implemented',
            502 => 'Bad Gateway',
            503 => 'Service Unavailable',
            504 => 'Gateway Timeout',
            505 => 'HTTP Version Not Supported',
            509 => 'Bandwidth Limit Exceeded',
        );
        return (isset($codes[$status])) ? $codes[$status] : '';
    }


    private function setHeader($status)
    {
        $status_header = 'HTTP/1.1 ' . $status . ' ' . $this->getStatusCodeMessage($status);
        $content_type = "application/json; charset=utf-8";
        header($status_header);
        header('Content-type: ' . $content_type);
        header('X-Powered-By: ' . "WantCode <WantCode.in>");
    }

    public function authenticate($params)
    {   
        // If IS_AUTH is set in constant then required to check header 
        if (IS_AUTH) {
            $encodeData = json_encode($params);
            $msg = array('response' => false, "message" => "Invalid token credentials");
            $hash = hash_hmac('sha256', $encodeData, Yii::$app->params['ApiTokenKey']);
            if (!isset($_SERVER['HTTP_X_API_TOKEN'])) {
                $this->setHeader(401);
                $this->response(401, $msg);
            }

            if ($_SERVER['HTTP_X_API_TOKEN'] != $hash) {
                $this->setHeader(401);
                $this->response(401, $msg);
            }
        }
    }

}

If IS_AUTH is set in constant then required to check header authentication is used or not. so it should be true for the authentication is needed.

Now you can make your controller like SiteController.php in which a action like this:


class SiteController extends ApiBaseController
{  
  public function actionAdd($domain)
    {
        $params = Yii::$app->request->post();
 $this->authenticate($params); // base controller function for auth
        $data = array("data"=>array("message"=>"success"));
 $this->response(200, $data);
    }
}

If you find any problem with these demo please don't hesitate to write a comment below .


No comments:

Post a Comment

Download the eBook of react js for beginners

ReactJS basically is an open-source JavaScript library which is used for building user interfaces specifically for single page applications...

Popular