I have two cakePHP apps on 2 different servers. One app is required to get data from the first one; I have succeeded to put the Restful architecture in place but I failed to implement an authentication procedure to the requests the server sends. I need to authenticate to secure the data. I have looked around on the web but can't seem to get it working. Can anyone point me to a resource / tutorial that explains this in detail.
What I would ultimately need would be a way to authenticate my server every time it sends a request to the other server. Any help would be appreciated.
I finally got it to work after some research; indeed one of the solutions is OAuth. In case you are facing the same problem, I can advise you this Plugin made for CakePHP.
In details what I did was put the OAuth Plugin into my API Server and I used it like so for my restful controller:
class RestObjectController extends AppController {
public $components = array('RequestHandler', 'OAuth.OAuth');
public $layout = FALSE;
public function token() {
$this->autoRender = false;
try {
$this->OAuth->grantAccessToken();
} catch (OAuth2ServerException $e) {
$e->sendHttpResponse();
}
}
public function index() {
$objects = $this->Object->find('all');
$this->set(array(
'objects' => $objects,
'_serialize' => array('objects')
));
}
The function RestObject.token() is what I would call to get an Access token which will be used to give me access to the Resources in my controller. (Note that by declaring OAuth in my controller components, all the resources within my controller will need an access token to be accessible).
So on the client Server I would get an access token in the following way:
public function acquireAccessToken(){
$this->autoRender = FALSE;
App::uses('HttpSocket', 'Network/Http');
$link = API_SERVER."rest_objects/token";
$data = array(
'grant_type' => 'client_credentials',
'client_id' => 'xxxx',
'client_secret' => 'xxxx'
);
$response = $httpSocket->post($link, $data);
if($response->code == 200){
$data = json_decode($response->body, true);
return $data['access_token'];
}
return FALSE;
}
This assumes that you have clients already set up as explained in the Plugin Doc (replace xxxx by the real values for the client credentials). Once I have my access token, all I have to do is use it as follows:
public function test(){
$this->layout = FALSE;
App::uses('HttpSocket', 'Network/Http');
$httpSocket = new HttpSocket();
if($access_token = $this->acquireAccessToken()){
$link = API_SERVER."rest_objects.json"; //For the index as e.g.
$data = array('access_token' => $access_token);
$response = $httpSocket->get($link, $data);
}
}
And here you have it! So start by reading the Oauth Specification to understand the Protocol (in particular the Obtaining Authorization part), see which protocol (can be different from the one I used) applies and adapt to your case by using the Plugin
Tutorial Here
Related
I'm building a webapp in Laravel which consumes multiple external REST API's, in which I have to authenticate myself and retrieve an access token before I'm able to do perform requests. I have built that like so:
ExampleAPIServiceProvider.php
class ExampleAPIServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(ExampleClient::class, function () {
return new ExampleClient(
accessToken: $this->getAccessToken()
);
});
}
private function getAccessToken(): ?AccessToken
{
return $this->accessToken ?? $this->requestAccessToken($clientId, $clientSecret);
}
}
This enables me to perform requests like so:
ExampleController.php
class ExampleController extends Controller
{
public function __construct(protected ExampleClient $client) { ... }
public function index()
{
$response = $this->client->getExamles();
}
}
I'm trying to consume another service, but this is a SOAP service. I know I can use the SoapClient(). If I understand correctly, with soap I first have to provide the url I'm trying to fetch data from and then do authentication. This is the example that the Soap Server I'm trying to consume provides:
try {
$soap = new SoapClient($webservice_url);
$res = $soap->Authenticate(array('accessKey' => $key));
if (!isset($res->AuthenticateResult)) exit();
$sess_id = $res->AuthenticateResult;
$xmlvar = new SoapVar('<ns1:xmlDoc>'.$xml.'</ns1:xmlDoc>', XSD_ANYXML);
$res = $soap->ProcessJournal(array('sessionID' => $sess_id, 'administrationID' => $admin_id, 'xmlDoc' => $xmlvar));
} catch (SoapFault $e) {
// throw exception...
}
I'd like to build a small wrapper client for this SoapClient and register that to the service provider as well. However I'm not sure how, as it seems that authentication happens after providing the webservice url. With the example from the Soap Server, it would mean that I'd have to provide the $sess_id and $administrationId each time I want to consume the service.
I think the result I'd like to have is to be able to call this from example the controller:
$this->soapClient->url($url)->ProcessJournal(...);
How do I go about doing registering it to the service provider? Or is there any other solution which would allow me to provide the authentication credentials only once?
Thanks in advance.
I am having issues setting up the "One-Click-App" oAuth on Bigcommerce with the BC PHP API library.
The issue at the moment is getting the actual auth token. I've tried various methods and believe it's down to the (Code/Context/Scope) get requests. They return as null every time.
I've tried both:
$request->query('code');
$request->get('code');
On the BC app launch screen I am presented with:
Trying to get property 'access_token' of non-object
Which of course is because the token is returning null.
Here is my controller in Laravel
namespace App\Http\Controllers;
use \Illuminate\Http\Request;
use Bigcommerce\Api\Client as Bigcommerce;
class BController extends Controller
{
//
public function index(Request $request)
{
$object = new \stdClass();
$object->client_id = 'xxxxxxxxxxxxxx';
$object->client_secret = 'xxxxxxxxxxxxxxxx';
$object->redirect_uri = 'https://linkto/process_oauth_result';
$object->code = $request->query('code');
$object->context = $request->query('context');
$object->scope = $request->query('scope');
Bigcommerce::useJson();
$authTokenResponse = Bigcommerce::getAuthToken($object);
// configure BC App
Bigcommerce::configure([
'client_id' => env('xxxxxxxxxxxxxx'),
'auth_token' => $authTokenResponse->access_token,
'store_hash' => 'xxxxxxx'
]);
Bigcommerce::verifyPeer(false);
return 'Success!';
}
}
Well it turns out that this works fine. I am using Runcloud to manage the app and the click-jacking option was blocking the iframe on the app screen!
How to set it up I read the tutorial http://code.tutsplus.com/tutorials/working-with-restful-services-in-codeigniter-2--net-8814. But I am unable to get the idea, I want more details. I am very new to CodeIgniter and to API.
I did the following steps from nettuts article
download both restclient and restserver and curl
I try to run examples from rest-server it does not show anything to me. I load my own controller and methods
REST SERVER :
this is the server that listen to client (restClient) request.
RESTServer has the Request methods :
POST()
GET()
PUT()
DELETE()
and these are use like index_put(); keep in mind when you called it from RESTClient ,you will called it like :
$this->index();
not
$this->index_put(); //because restserver it self recognize the nature of request through header.
Here is a simple example:
RESTClient:
function request_test() {
$this->load->library('rest', array(
'server' => 'http://restserver.com/customapi/api/',
//when not use keys delete these two liness below
'api_key' => 'b35f83d49cf0585c6a104476b9dc3694eee1ec4e',
'api_name' => 'X-API-KEY',
));
$created_key = $this->rest->post('clientRequest', array(
'id' => '1',
'CustomerId' => '1',
'amount' => '2450',
'operatorName' => 'Jondoe',
), 'json');
print_r($created_key);
die;
}
Make sure you loaded rest library.
RESTSERVER:
<?php
require APPPATH . '/libraries/REST_Controller.php';
class api extends REST_Controller {
public function clientRequest_post() {
//to get header
$headers=array();
foreach (getallheaders() as $name => $value) {
$headers[$name] = $value;
}
//to get post data
$entityBody = file_get_contents('php://input', 'r');
parse_str($entityBody , $post_data);
//giving response back to client
$this->response('success', 200);
}
}
configuration config/Rest.php:
//if you need no authentication see it's different option in the same file
$config['rest_auth'] = false;
//for enabling/disabling API_KEYS
$config['rest_enable_keys'] = FALSE;
I have already written an application in a procedural way and am trying to move into into a Laravel framework. I'm having trouble with the SOAP exchange section as I am getting an ID value that authenticates the user but cannot access that value (as a cookie) later in the program to authenticate the search.
Here is my code so far:
<?php namespace App;
use Artisaninweb\SoapWrapper\Facades\SoapWrapper;
use Illuminate\Http\RedirectResponse;
class SoapController {
private $auth_response;
private $cookie;
private $search_client;
private $search_response;
public function soapExchange() {
// create SOAP client and add service details
SoapWrapper::add(function ($service) {
$service
->name('WoSAuthenticate')
->wsdl('http://search.webofknowledge.com/esti/wokmws/ws/WOKMWSAuthenticate?wsdl')
->trace(true)
->cache(WSDL_CACHE_NONE);
});
SoapWrapper::service('WoSAuthenticate', function($service) {
// call authenticate() method to get SID cookie
$auth_response = $service->call('authenticate', []);
$cookie = $auth_response->return;
// test for cookie return
// print($cookie);
});
// create SOAP client and add service details
$search_client = new SoapWrapper;
$search_client::add(function ($service) {
$service
->name('WoSSearch')
->wsdl('http://search.webofknowledge.com/esti/wokmws/ws/WokSearch?wsdl')
->trace(true)
->cache(WSDL_CACHE_NONE);
});
if (isset($auth_response->return)) {
// if there is an SID returned then add it to the cookie attribute of the search client
$search_client->__setCookie('SID', $cookie);
} else {
// route to relevant view to display throttle error
return redirect('throttle');
}
}
}
I am successfully retrieving the response from the Web API call and getting a code to authenticate the user, saved as $cookie. However, I need then to create another SoapWrapper for performing the search and this needs the ID code attached by using the __setCookie method. If nothing is returned by the authenticate call then it redirects to an error message via throttle.blade.php elsewhere.
Surely there is a way to return a value created from a function so that it can be used elsewhere?
** EDIT **
Looked into employing SoapClient instead and including all operations within a single function. It all relates to a specific Web API anyway so I guess separation of concerns is not so much of an issue. FYI the new class I am trying is this:
<?php namespace App\Models;
use SoapClient;
use Illuminate\Http\RedirectResponse;
class SoapWrapper {
public function soapExchange() {
// set WSDL for authentication and create new SOAP client
$auth_url = "http://search.webofknowledge.com/esti/wokmws/ws/WOKMWSAuthenticate?wsdl";
// array options are temporary and used to track request & response data
$auth_client = #new SoapClient($auth_url);
// set WSDL for search and create new SOAP client
$search_url = "http://search.webofknowledge.com/esti/wokmws/ws/WokSearch?wsdl";
// array options are temporary and used to track request & response data
$search_client = #new SoapClient($search_url);
// run 'authenticate' method and store as variable
$auth_response = $auth_client->authenticate();
// call 'setCookie' method on '$search_client' storing SID (Session ID) as the response (value) given from the 'authenticate' method
// check if an SID has been set, if not it means Throttle server has stopped the query, therefore display error message
if (isset($auth_response->return)) {
$search_client->__setCookie('SID',$auth_response->return);
} else {
return Redirect::route('throttle');
}
}
}
Maybe try $GLOBALS?
<?php
$GLOBALS[data] = "something";
function abc(){
echo $GLOBALS[data];
}
?>
use Artisaninweb\SoapWrapper\Facades\SoapWrapper;
class SoapController extends Controller {
public $resultSoapStatus;
public $resultSoapAuthority;
public function heySoap{
SoapWrapper::add(function ($service) ...
$data = [
'MerchantID' => $MerchantID,
'Amount' => $Amount,
'Description' => $Description,
'Email' => $Email,
'Mobile' => $Mobile,
'CallbackURL' => $CallbackURL
];
SoapWrapper::service('test', function ($service) use ($data) {
$resultSoap = $service->call('PaymentRequest', [$data]);
$this->resultSoapStatus = $resultSoap->Status;
$this->resultSoapAuthority = $resultSoap->Authority;
});
if($this->resultSoapStatus == 100 && strlen($this->resultSoapAuthority) == 36)
{
//Do Something
}
else
{
return Redirect::back();
}
}
}
Enjoy bro
I have two one question about the Fat Free Framework.
First of all, how can i use multiple parameters(tokens in fat free framework) in a GET request?
Or, is there only 1 token possible per REST GET request, and should one handle additional
arguments as a regular GET request, for example:
domain/rest/somedata/5231?param1=value1¶m2=value2
where the ?param1=value1¶m2=value2 should be 'manually' parsed, not by a framework?
Is it at all possible to build a RESTful API with Fat Free Framework and also have some area's or routes needing authentication? if so, how?
I just stumbled upon this related question: REST API Best practices: Where to put parameters?
[edit]: i've found out that it is indeed possible to have authentication with fat free framework using several methods. However, they seem not very well documented (at least not on their github wiki).
[edit2] Since it's only very basic authentication, for now i'm using this:
function beforeRoute($f3,$params) {
$url = $params[0];
$parsed_key = parse_str(parse_url($url, PHP_URL_QUERY));
if (isset($apikey)){
// check if apikey is in database
$authenticated = false;
foreach(R::find('apikey') as $key_bean) {
if($key_bean->key == $apikey) {
$authenticated = true;
break;
}
}
if($authenticated == false) $f3->error(403);
} else {
$f3->error(403);
}
}
I'm looking for documentation on the basic http authentication method!
The auth class always authenticates you against a mapper. Feel free to use F3's Jig, Mongo or SQL.
$db = new DB\SQL('mysql:host=localhost;dbname=mydb', 'dbuser', '1234');
$mapper = new DB\SQL\Mapper($db, 'users');
$auth = new Auth($mapper, array('id'=>'username','pw'=>'password'));
if($auth->basic())
return true;
password and username are field names in the database. id and pw are internal used by the auth class. I recommend checking the auth class code and the unit tests in the dev branch on Github.
An simple example would be something like...
Username: admin, Password: 123
// Create users table using Jig.
$db = new \DB\Jig('data/');
$users = array(
0 => array('username' => 'admin', 'password' => '202cb962ac59075b964b07152d234b70'),
);
$db->write('users', $users);
$db_mapper = new \DB\Jig\Mapper($db, 'users');
$auth = new \Auth($db_mapper, array('id' => 'username', 'pw' => 'password'));
// Callback function because of md5 stored password.
function chkauth($pw) {
return md5($pw);
}
$auth->basic('chkauth');