Hey,
I think I have just the thing you need. One of my clients has an API only for internal use.
Everything is only for local network, so I needed to know which user did the request, nothing more...
For this I used Authorization: Bearer UNIQUE_TOKEN
header which is sent with every API request.
Sample code that does the job:
BaseController - this is the controller that all other API Controllers extend.
public function beforeExecuteRoute()
{
// Route names that do not need authorization
$authorizeExceptions = [
'api-documentation'
];
if (!in_array($this->router->getMatchedRoute()->getName(), $authorizeExceptions)) {
// Authorize
$result = $this->authorize();
if (is_null($result)) {
$this->_response['messages'] = 'Please authorize with valid API token!';
$this->_response['statusCode'] = 401;
$this->afterExecuteRoute();
die();
}
}
// We accept only application/json content in POST and PUT methods
if (in_array($this->request->getMethod(), ['POST', 'PUT']) AND $this->request->getHeader('Content-Type') != 'application/json') {
$this->_response['messages'] = 'Only application/json is accepted for Content-Type in POST requests.';
$this->_response['statusCode'] = 400;
$this->afterExecuteRoute();
die();
}
}
// Return the API response.
public function afterExecuteRoute()
{
// Status code & Response header
$this->_response['statusMessage'] = $this->_statusCodes[$this->_response['statusCode']];
$this->response->setStatusCode($this->_response['statusCode'], $this->_statusCodes[$this->_response['statusCode']]);
$this->response->setHeader('Access-Control-Allow-Origin', '*');
$this->response->setHeader('X-Content-Type-Options', 'nosniff');
$this->response->setHeader('X-Frame-Options', 'deny');
$this->response->setHeader('Content-Security-Policy', 'default-src \'none\'');
// Set content
$this->response->setContentType('application/json', 'UTF-8');
$this->response->setJsonContent($this->_response, JSON_UNESCAPED_UNICODE);
// Log
if (!is_null($this->user) AND $this->user->id != 1) {
$request = $this->request->get();
$request['accept'] = $this->request->getHeader('Accept');
$this->db->insertAsDict('api_access_logs', [
'api_user_id' => $this->user->id,
'endpoint' => $this->request->getMethod() .' '. getCurrentUrl(false, false),
'request' => json_encode($request, JSON_UNESCAPED_UNICODE),
'response' => json_encode($this->_response, JSON_UNESCAPED_UNICODE),
]);
}
return $this->response->send();
}
// Check if valid Token is given
private function authorize()
{
$this->user = null;
$authorizationHeader = $this->request->getHeader('Authorization');
if ($authorizationHeader AND preg_match('/Bearer\s(\S+)/', $authorizationHeader, $matches)) {
$tokenParts = explode('|', \Helpers\Common::decodeString($matches[1]));
// For now token has 3 parts. Update here if you modify token
// IMPORTANT: Here you can make your custom logic to check for valid token
if (count($tokenParts) === 3) {
$this->user = (object) [
'id' => $tokenParts[0],
'level' => $tokenParts[1],
];
}
}
return $this->user;
}
In the authorize()
method I'm encoding/decoding the token with \Phalcon\Crypt
library.
class Common
{
private static $cryptKey = 'i$1^&/:%[email protected]!R1Q<@{([email protected]*!<7u|R2~0';
public static function encodeString($string)
{
return (new \Phalcon\Crypt)->encryptBase64($string, self::$cryptKey, true);
}
public static function decodeString($string)
{
return (new \Phalcon\Crypt)->decryptBase64($string, self::$cryptKey, true);
}
...
}