Fabric Digits oauth authentication in PHP - php

I've got an app which works with Digits as authentication.
Client-side works perfectly, but I'm not able to make the server user authentication through oAuth.
My server is developed with Laravel, so it's PHP.
My endpoint is under https, so everything should be ready to make the call.

I solved by myself!
Here's the code that makes correctly the authentication through Digits O-Auth.
The function that makes authentication is inside an AuthManager class and I call in this way:
$obj = AuthManager::verifyUser($request->header('X-Auth-Service-Provider'),$request->header("X-Verify-Credentials-Authorization"));
And here's the function that makes the magic:
public static function verifyUser ($xAuthServiceProvider, $xVerifyCredentialsAuthorization)
{
$curl = curl_init();
curl_setopt($curl,CURLOPT_URL, $xAuthServiceProvider);
curl_setopt($curl,CURLOPT_HTTPHEADER, array(
'Content-length: 0',
'Content-type: application/json',
'Authorization: '.$xVerifyCredentialsAuthorization,
));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$content = curl_exec($curl);
$info = curl_getinfo($curl);
curl_close($curl);
$obj = json_decode($content, true);
return $obj;
}
If you have any problem, don't hesitate to write here!
EDIT WITH EXAMPLE FUNCTION ABOUT HOW TO GET DATA FROM O-AUTH
public function authenticate (Request $request)
{
$obj = AuthManager::verifyUser($request->header('X-Auth-Service-Provider'),$request->header("X-Verify-Credentials-Authorization"));
if(isset($obj["errors"]))
{
return "error!";
}
$digits_token = $obj["access_token"]["token"];
$digitsId = $obj["id"];
/*
the variables above are returned by O-auth server-server authentication
*/
}

Related

How to test the client in the absence of API?

I decided to write a client that sends http requests to the API. There are 3 types of requests: GET, POST, PUT. We need to write unit tests using phpunit, which will allow us to test the functionality without writing an API. My first thought was to use a mock object. Having read enough literature, I can’t understand in any way how to do this. As I understand it, I need to make a stub for the API wherever my requests go. Please tell me in which direction to move in order to solve the problem.
<?php
namespace Client;
class CurlClient implements iClient
{
private $domain;
public function __construct($domain = "http://example.com")
{
$this->domain = $domain;
}
public function getAllComments()
{
$ch = curl_init($this->domain.'/comments');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$comments = curl_exec($ch);
$comments = json_decode($comments);
curl_close($ch);
return $comments;
}
public function addNewComment($data)
{
$ch = curl_init($this->domain.'/comment');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$statusCode = (string)$statusCode;
$statusCode = (int)$statusCode[0];
curl_close($ch);
return $statusCode == 2 ? true : false;
}
public function updateComment($id, $data)
{
$ch = curl_init($this->domain.'/comment/'.$id);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$statusCode = (string)$statusCode;
$statusCode = (int)$statusCode[0];
curl_close($ch);
return $statusCode == 2 ? true : false;
}
}
Here is a simple mock example using phpunit. phpunit's mocking capabilities are extensive, for more info phpunit documentation - test doubles
<?php
use PHPUnit\Framework\TestCase;
// Use the getMockBuilder() method that is provided by the
// PHPUnit\Framework\TestCase class to set up a mock object
// for the CurlClient object.
class CurlClientTest extends TestCase
{
public function testAddNewComment()
{
// Create a mock for the CurlClient class,
// only mock the update() method.
$client = $this->getMockBuilder(CurlClient::class)
->setMethods(['addNewComment'])
->getMock();
$map = [
['hello', true],
[123, false]
];
$client->method('addNewComment')->will($this->returnValueMap($map));
$this->assertEquals(true, $client->addNewComment('hello'));
$this->assertEquals(false, $client->addNewComment(123));
}
}

Laravel curl in middleware gets added to response

I have a service, that needs to authenticate via another service.
For this I setup a Middleware that extracts the Authorization header out of my initial request, and then creates a curl request to the Auth Service with the header set.
public function handle($request, Closure $next) {
$authHeader = $request->header('Authorization');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://api.user.eventmanager.app/getAccess");
curl_setopt($ch,CURLOPT_HTTPHEADER,array(
'Authorization: ' . $authHeader,
'Origin: http://api.asdf.rerere.app'
));
$result = curl_exec($ch);
if($result) {
curl_close($ch);
return $next($request);
} else {
curl_close($ch);
return response("Invalid Token or expired Token", 401);
}
}
The request returns the requested ressource as expected, but also adds in the User object in the response (the one I get via the curl request, I want to do further checking with the user object in the middleware, but I dont want it returned to the inital request).
Here is what my controller for the response I want looks like:
public function show($id)
{
$event = Event::with('timeTableEntries', 'venue', 'bands')->find($id);
if(!$event) {
return $this->respondNotFound('Event does not exist!');
}
return $this->respond([
'data' => $this->eventTransformer->transform($event)
]);
}
Somehow the User Object from the curl ends up in my respons.
Any idea why this happens?
You're not returning transfer on your curl options, which stops the response from outputting and returns the string instead.
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
as per http://php.net/manual/en/function.curl-setopt.php

how to call restful zend controller usin curl in php?

<?php
require_once 'Zend/Session/Namespace.php';
class ApiController extends Zend_Rest_Controller
{
public function init()
{
$this->_helper->layout->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
}
public function indexAction()
{
$query=$this->getRequest()->getParam('query');
$this->getResponse()
->appendBody("hi");
$this->getResponse()->setHttpResponseCode(200);
}
public function getAction()
{
$query=$this->getRequest()->getParam('id');
$this->getResponse()
->appendBody($query);
}
public function postAction()
{
$this->getResponse()
->setHttpResponseCode(200)
->appendBody("From postAction() creating the requested article");
}
public function putAction()
{
$this->getResponse()
->appendBody("From putAction() updating the requested article");
}
public function deleteAction()
{
$this->getResponse()
->appendBody("From deleteAction() deleting the requested article");
}
}
Above is my REST API I am trying to call it from php curl but I don't know how to call post method.
I have also made entry in bootsrap to default module\ using rest route.
Here is a snippet of my code:
<?php
$ch = curl_init('http://apanel3.newfront.local/api');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 4);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)");
$curlresult = curl_exec($curl_connection);
print_r($curlresult);
?>
I am trying to call my api using following curl code. It is calling indexAction. Even thought i have set curlopt_post to true, I am not getting desired output.
I believe there are lot of examples for php curl + post. Do you know how to access your actions (make http calls generally, without curl) ?
Here is another answer to the link to the similar question.
If you are trying to access your API from another zend based application and want to use zend inbuilt method then, you should check Zend_Http_Client there is an adapter for curl if you want to specifically use curl adapter.
EDIT:
On your client side:
<?php
//
// A very simple PHP example that sends a HTTP POST to a remote site
//
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "'http://apanel3.newfront.local/api'");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "query='where post_parameter = query'");
// receive server response ...
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($ch);
curl_close($ch);
// further processing ....
if($server_output == 'OK') {
echo 'Test passed';
} else {
echo $server_output;
}
?>
On your API side for indexAction:
public function indexAction()
{
$query=$this->getRequest()->getParam('query');
if($this->getRequest()->isPost()) {
// method == post, do your processing whatever required here ....
$this->_forward('post',null,null,$query);
} elseif ($this->getRequest()->getMethod() == 'GET') {
// method == get
$this->_forward('get',null,null,$query);
}else {
// bad request response code 400
$this->getResponse()
->appendBody("not a valid request");
$this->getResponse()->setHttpResponseCode(400);
}
}
Edit:
My mistake I didn't realize you were extending Zend_Rest_Controller, your code for controller seems fine. Now you probably know about making curl request via PHP.
On how to make PUT Request please check this question

Symfony2 - How to perform an external Request

Using Symfony2, I need to access an external API based on HTTPS.
How can I call an external URI and manage the response to "play" with it. For example, to render a success or a failure message?
I am thinking in something like (note that performRequest is a completely invented method):
$response = $this -> performRequest("www.someapi.com?param1=A&param2=B");
if ($response -> getError() == 0){
// Do something good
}else{
// Do something too bad
}
I have been reading about Buzz and other clients. But I guess that Symfony2 should be able to do it by its own.
I'd suggest using CURL:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'www.someapi.com?param1=A&param2=B');
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/json')); // Assuming you're requesting JSON
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
// If using JSON...
$data = json_decode($response);
Note: The php on your web server must have the php5-curl library installed.
Assuming the API request is returning JSON data, this page may be useful.
This doesn't use any code that is specific to Symfony2. There may well be a bundle that can simplify this process for you, but if there is I don't know about it.
Symfony doesn't have a built-in service for this, but this is a perfect opportunity to create your own, using the dependency injection framework. What you can do here is write a service to manage the external call. Let's call the service "http".
First, write a class with a performRequest() method:
namespace MyBundle\Service;
class Http
{
public function performRequest($siteUrl)
{
// Code to make the external request goes here
// ...probably using cUrl
}
}
Register it as a service in app/config/config.yml:
services:
http:
class: MyBundle\Service\Http
Now your controller has access to a service called "http". Symfony manages a single instance of this class in the "container", and you can access it via $this->get("http"):
class MyController
{
$response = $this->get("http")->performRequest("www.something.com");
...
}
Best client that I know is: http://docs.guzzlephp.org/en/latest/
There is already bundle that integrates it into Symfony2 project:
https://github.com/8p/GuzzleBundle
$client = $this->get('guzzle.client');
// send an asynchronous request.
$request = $client->createRequest('GET', 'http://httpbin.org', ['future' => true]);
// callback
$client->send($request)->then(function ($response) {
echo 'I completed! ' . $response;
});
// optional parameters
$response = $client->get('http://httpbin.org/get', [
'headers' => ['X-Foo-Header' => 'value'],
'query' => ['foo' => 'bar']
]);
$code = $response->getStatusCode();
$body = $response->getBody();
// json response
$response = $client->get('http://httpbin.org/get');
$json = $response->json();
// extra methods
$response = $client->delete('http://httpbin.org/delete');
$response = $client->head('http://httpbin.org/get');
$response = $client->options('http://httpbin.org/get');
$response = $client->patch('http://httpbin.org/patch');
$response = $client->post('http://httpbin.org/post');
$response = $client->put('http://httpbin.org/put');
More info can be found on: http://docs.guzzlephp.org/en/latest/index.html
https://github.com/sensio/SensioBuzzBundle seems to be what you are looking for.
It implements the Kris Wallsmith buzz library to perform HTTP requests.
I'll let you read the doc on the github page, usage is pretty basic:
$buzz = $this->container->get('buzz');
$response = $buzz->get('http://google.com');
echo $response->getContent();
Symfony does not have its own rest client, but as you already mentioned there are a couple of bundles. This one is my prefered one:
https://github.com/CircleOfNice/CiRestClientBundle
$restClient = $this->container->get('ci.restclient');
$restClient->get('http://www.someUrl.com');
$restClient->post('http://www.someUrl.com', 'somePayload');
$restClient->put('http://www.someUrl.com', 'somePayload');
$restClient->delete('http://www.someUrl.com');
$restClient->patch('http://www.someUrl.com', 'somePayload');
$restClient->head('http://www.someUrl.com');
$restClient->options('http://www.someUrl.com', 'somePayload');
$restClient->trace('http://www.someUrl.com');
$restClient->connect('http://www.someUrl.com');
You send the request via
$response = $restclient->get($url);
and get a Symfony response object.
Then you can get the status code via
$httpCode = $response-> getStatusCode();
Your code would look like:
$restClient = $this->container->get('ci.restclient');
if ($restClient->get('http://www.yourUrl.com')->getStatusCode !== 200) {
// no error
} else {
// error
}
Use the HttpClient class to create the low-level HTTP client that makes requests, like the following GET request:
use Symfony\Component\HttpClient\HttpClient;
$client = HttpClient::create();
$response = $client->request('GET', 'https://api.github.com/repos/symfony/symfony-docs');
$statusCode = $response->getStatusCode();
// $statusCode = 200
$contentType = $response->getHeaders()['content-type'][0];
// $contentType = 'application/json'
$content = $response->getContent();
// $content = '{"id":521583, "name":"symfony-docs", ...}'
$content = $response->toArray();
// $content = ['id' => 521583, 'name' => 'symfony-docs', ...]
This is compatible with Symfony 5. Symfony Manual on this topic: The HttpClient Component

Is per-request, stateless HTTP Basic/Digest authentication possible in CakePHP 2.0?

The manual is unclear as to how to implement this (it assumes you already know what exactly you're doing and in some cases feels like an afterthought), and I've been scratching my head for a fair while trying to figure it out.
The problem: authentication via HTTP auth headers for all API requests
As far as I've been able to test, I can use Basic auth and the normal form based login in CakePHP, but only by first hitting up the login action I define in the Auth component. This is fine when I'm accessing the site directly, and works as expected (with the exception of Digest, which appears to be utterly buggered). Via cURL, though, I've had no luck unless I'm already logged in.
Obviously, for an API, this is far from ideal. I don't want to post a request to /login before doing what I want to do, and I can't expect a user to log in manually so Cake has a cookie to read. It needs to be stateless.
Any attempt to supply authentication credentials along with each request I make (via cURL) is ignored and I get a 403 error in return. Neither the login method or any of the Auth classes are touched.
What do I need to do to make Cake behave like an actual API and allow me to authorise statelessly on a per request basis? Am I going to have to roll my own solution?
I have a centralized API that allows for user authentication via HTTP Digest and requires users to login for many user related functions. The way CakePHP forces a login is by checking if the action requires login, redirecting to your login action (defaults to /users/login), then you can redirect back.
I created my API by doing the following:
//Config/routes.php
///////////////////////////
/**
* Users Controller routes for REST API
*/
Router::mapResources('users');
/**
* Parses extensions for data serialization
*/
Router::parseExtensions();
//Controller/UserController.php
////////////////////////////////
<?php
App::uses('DigestAuthenticate', 'Controller/Component/Auth/');
class UsersController extends AppController {
var $name = 'Users';
//Login callback
function login() {
//dont render for login, just a call back for auth
$this->autoRender = false;
if ($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
}
}
//GET /users.[xml|json]
//this is the basic call that tests user authentication
//basically a login then echo
function index() {
if ($this->Auth->login()) {
$user = $this->Auth->user();
$this->User->id = $user['id'];
$this->User->saveField('last_login', date('Y-m-d H:i:s'));
$this->set('response', array(
'response' => array(
'code' => 'users_auth_success',
'message' => 'User has passed authentication',
'data' => $user
)
));
//will serialize to xml or json based on extension
$this->set('_serialize', 'response');
}
}
}
?>
You can then use this API in something like:
$c = curl_init($uri . '.' . $this->_format);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_USERPWD, $login['user'] . ':' . $login['pass']);
curl_setopt($c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($c, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($c);
$info = curl_getinfo($c);
curl_close($c);
if($info['http_code'] == $this->_http_codes['OK']) {
//success
if($this->_format == 'xml')
$response = Xml::toArray(Xml::build($response));
else//JSON
$response = json_decode($response);
return $response['response']['data'];
} else if($info['http_code'] == $this->_http_codes['Unauthorized']) {
return false;
} else {
return null;
}

Categories