I'm building a PHP application from scratch (using Kohana3 framework). I'm going to architect it so that I can use an API to access the data internally. At the same time, I want to eventually offer it to the public.
I plan on using the RESTful access method. However, I'm having a hard time finding clear information on how to properly secure the API. In other words, how do I implement API signatures and access?
You could try frapi. It will quickly allow you to build your RESTful API, which you can then use for your application, and at a later date expose the same API publicly.
OAuth would be a good choice. So would a single key/value pair. You might also want to look at Mashape but not entirely sure it fits what you are trying to do.
I think a good place to start would be reading over general information about digital signing. Wikipedia is a great resource http://en.wikipedia.org/wiki/Public_Key_Infrastructure or http://en.wikipedia.org/wiki/X.509.
On a basic level I would give each client a private Key. In the client library I would encrypt the key. When a client makes a request verify that the key is the one that you issued to that particular client.
Take a look at 3scale (http://www.3scale.net/) to do this - it handles authentication, access control, policies, rate limits etc. and is free for significant traffic. We have a PHP module to plugin to the system to enable these features. (Disclaimer - I work there - but hope it's useful!)
Your question is a bit bigger than this; but I can offer one small observation about REST.
I have found with REST, that is that it is best to use artificial keys for the underlying data model, rather than natural keys.
For example, consider the RESTfull url: https://server/yourApp/viewUser/1234.html this would show the user with id 1234. However if you used natural keys you might have a URL something like this https://server/yourApp/viewUser/Bob.html or worse if instead of Bob its "Bob X" or "Bob?key=Value". You don't want to have to think about generating invalid URLs.
I have made a PHP REST API using CodeIgniter with Basic Authentication, (providing "company id" and "API Key" as username/password). Later we found that it was necessary to provision session keys that were directly related to an API Key, only with an expiration time.
Basically, we queried different types of data in our datastore (nosql variety :) depending on what the "method" was provided in the URL. We accessed this by using the "segment" ability provided by CodeIgniter.
Then we wrapped each response with a "json_encode" that was returned and we also used an HTTPS connection for security.
For the client class we wrapped everything in calls such $client->get_my_data($api_key), with a layer underneath using PHP Libcurl, which works really well to provide the Basic Auth.
Hope this helps,
CURL_GET
private function curl_get($url, $apikey, $co)
{
$curl_handle = curl_init();
curl_setopt($curl_handle, CURLOPT_URL, $url);
curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl_handle, CURLOPT_USERPWD, $co.":".$apikey);
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl_handle, CURLOPT_DNS_USE_GLOBAL_CACHE, FALSE);
$buffer = curl_exec($curl_handle);
$error = curl_error($curl_handle);
curl_close($curl_handle);
// check for success or failure
if (empty($buffer)) {
//echo 'Something went wrong :( error: '.$error.'<Br>';
} else {
return $buffer;
}
}
Related
I'm using Firebase to do a small project and while testing things I discovered I can do cURL requests from any server to my Firebase Database (tested on an online php tester), so I'm considering this is a security flaw for my project and I have been looking for a method to add some kind of password for cURL requests, but I found nothing, at least nothing I could understand. I know firebase have rules to manage who can read or write on my database, but I didnt find something that could filter requests by server or only allow requests that have an special password sent as parameter.
So my question is if there is a way to do something like that I could use on my project so only cURL requests made for me would work.
Here it is one of my cURL requests, in case it helps for resolving my problem.
$url = "https://mydatabase.firebaseio.com/profile/messages/".$_COOKIE['cookiename'].".json";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
Thanks in advance for helping me out.
UPDATE: I found this, I think it could be the thing I need, but Im missing the part where I tell the database to ask for the access token. https://firebase.google.com/docs/database/rest/auth
One solution is to use the Firebase Auth REST API.
In particular, "you can sign in a user with an email and password by issuing an HTTP POST request to the Auth verifyPassword endpoint", see here.
Then you can use the user's uid in your Firebase security rules, in order to protect your database.
You should read and understand the documentation for the REST API. If you want to bypass security rules that would normally apply to web and mobile users, you will need to generate an OAuth token for a service account that has permissions to access your database, and use that in your requests.
If you don't want public access to your database, you will have to set up security rules to limit that. To stop all public access, your rules should be:
{
"rules": {
".read": false,
".write": false
}
}
I'm trying to add a new Deal with the Pipedrive API.
To do so I've followed this tutorial: http://support.pipedrive.com/customer/portal/articles/1271064-how-to-send-in-deals-using-a-web-form
But there's something I didn't understand:
"Email API gives your company a special email address you can use to
automate lead generation and adding of new contacts and
organizations."
Where can I get this email address, there's no other mention of it at the tutorial?
Since I'm unable to follow the tutorial I'm trying to add a new deal with cURL, this is the code:
<?php
$deal = array("item_type" => "deal","stage_id" => 1,"title" => "Atendimento Web Site","organization" => "Company","owner" => "johndoe#company.com.br","visible_to" => 2,"person" => array("name" => $nome,"email" => $email,"organization" => $empresa,"phone" => $tel));
$deal_string = json_encode($deal);
$ch = curl_init('https://api.pipedrive.com/v1/deals?api_token=TOKEN');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $deal_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json, charset=UTF-8',
'Content-Length: ' .strlen($deal_string))
);
echo $deal_string;
echo curl_exec($ch);
?>
And this is what I get:
iten sent -> {"item_type":"deal","stage_id":1,"title":"Atendimento","organization":"Company","owner":"owner#mail.com.br","visible_to":2,"person":{"name":"Jo\u00e3o Neto","email":"mail#mail.com.br","organization":"Company 2","phone":"7112345678"}}
return from api -> {"success":false,"error":"Deal title must be given.","data":null,"additional_data":null}
Where's the error?
About the email support it's true that you are mixing two thing, although it was also happened to me the first time. I admit it would seem strange, an API in which you can use emails.
Anyway, I was working on a simple integration between Pipedrive and another platform and I used the full REST API.
I noticed every time you have an error creating a Deal or you make a mistake in the Json (even if title is ok), you always get the same answer "error":"Deal title must be given.". Of courses it won't help you too much.
So, I recommend you to use some tools like RESTClient for Firefox to simplify the problem at the beginning or even Firebug to sniff it from https://developers.pipedrive.com/v1 making use of their tools to understand the request a little bit better. After that, you can do it more complex.
I am putting you a screenshot in which you can see the simplest example. I hope it will be useful for anyone
I'd receive an email from Pipedrive Support with a full anwser.
*Hi,
Thanks for reaching out!
I'm sorry to hear about the trouble!
So you're mixing up two completely separate things. You're sending in the JSON object needed for the Email API into the REST API.
You have 2 options.
You could go full on with the email API. To do this you need to log into your Pipedrive account, navigate to the Settings, Features page and enable the Email API feature. Then click through to the email API page and get the email address you need to send the object to. And then alter your PHP code to send in that object to that email address as a plain text email. No curl or API token needed for that.
You could clean up the data object you're sending in with the REST API. But you need to understand that the REST API works a little different from the Email API. So you can't just send in the person object along with the deal. You would first need to POST in the person with all the details to the persons endpoint and get back the ID. You can then use the person ID in the deals POST.
I hope this helps
Martin Henk | Co-Founder, Head of Customer Support
Pipedrive*
So I'm trying to get details of a specific venue using PHP. Here's my code that attempts to use a GET request to the Foursquare API to return results and then process them as JSON and display the name, address and city:
$curlhandle = curl_init();
curl_setopt($curlhandle, CURLOPT_URL, "https://api.foursquare.com/v2/venues/4b522afaf964a5200b6d27e3");
curl_setopt($curlhandle, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($curlhandle);
curl_close($curlhandle);
$json = json_decode($response);
foreach ($json->groups[0]->venues as $result)
{
echo $result->name.' - '.$result->address.' '.$result->city."<p />";
}
What am I doing wrong? I'm completely new to PHP and the Foursquare API so it could be something glaringly obvious.
You don't need to authenticate using the OAuth flow to get venue information, but you do need to add your Client ID and Client Secret to the API call.
So, the URL should be something like:
"https://api.foursquare.com/v2/venues/4b522afaf964a5200b6d27e3?client_id=CLIENT_ID&client_secret=CLIENT_SECRET
In JavaScript, the URL should be
`https://api.foursquare.com/v2/venues/${venue_id}?client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&v=20180323`
Note, I am using template literals and don't forget v=20180323 because the Foursquare API no longer supports requests that do not pass in a version parameter. Of course, you can modify the version number to keep updated.
You need to authenticate your request, if you go to the URL you get this.
{"meta":{"code":400,"errorType":"invalid_auth","errorDetail":"Missing access credentials. See https:\/\/developer.foursquare.com\/docs\/oauth.html for details."},"response":{}}
So I'd say you need to authenticate by following this: https://developer.foursquare.com/overview/auth.html
This worked for me: (on Python)
url = 'https://api.foursquare.com/v2/venues/{0}'.format(self.placeid)
params = dict(
client_id=self.clientid,
client_secret=self.clientsecret,
v='20170801'
)
r = requests.get(url=url, params=params)
I have a text file with 5,000 Twitter Users :-
JRJSHEARD
KMM_1979
ELMOCHLOE
ANNIEMMERSON
PATLOCKLEY
LISSYNUMBER
CAL32INCHSCREEN
PRINGLEDUDE
CORESMUSIC
I have found this API http://api.twitter.com/1/users/show.xml?screen_name=JRJSHEARD which is really useful and just what I need.
How would I write a php function to loop through the user names in a text file and append their bio found between these tags (<description> </description>).
Is this possible? Any help would be gratefully received.
If you want to harvest user info in bulk from Twitter use users/lookup rather than users/show. The users/lookup API call returns 100 user objects at a time, and you can either pass the user IDs or the screen names when you make the call, however you will need to authenticate using OAuth in order to use it.
I recommend using JSON since it is a much more lightweight document format than XML. You will typically transfer only about 1/3 to 1/2 as much data over the wire, and I find that (in my experience) Twitter times-out less often when serving JSON.
http://api.twitter.com/1/users/lookup.json?screen_name=JRJSHEARD,KMM_1979,ELMOCHLOE
That's the direct API call, but if you're just starting out, what I would recommend is using a Twitter service implementation rather than try to do all the heavy lifting yourself. I'm not a PHP person, but my PHP-using Twitter buddies recommend Zend - http://framework.zend.com/manual/en/zend.service.twitter.html
$api = "http://api.twitter.com/1/users/show.xml?screen_name=";
$users = file("users.txt", FILE_IGNORE_NEW_LINES);
$i = 0;
foreach($users as $user){
$data = curl("$api$user");
preg_match("#<description>(.*?)</description>#is", $data, $matches);
$bio[$i]["user"] = $user;
$bio[$i]["description"] = $matches[1];
$i++;
}
function curl($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_close($ch);
return curl_exec($ch);
}
I already did a lot of research on this topic and have implemented a lot of solutions myself.
Including OpenID, Facebook Connect (using the old Rest API and the new Graph OAuth 2.0 API), Sign in with twitter (which has been upgraded to fully qualified OpenID by now as far as I know), and so on...
But what I'm still missing is the perfect all in one solution.
During my research I stumbled about some interesting projects:
Janrain (formerly RPX) - a commercial solution
Gigya - a free but externally hosted solution with javascript and rest apis
AnyOpenID - a free solution for clients, commercial for websites
But I don't want to rely on an external provider and I would like a free solution as well, so I am not limited in implementation.
I have also seen developers implementing one service after another dutifully following the providers instructions and setting up models and database tables for everything.
Of course this will work but it is a shitload of work and always needs development and changes in your application etc.
What I am looking for is an abstraction layer that takes all the services out there to one standard that can be integrated in my website. Once a new service appears I only want to add one model that deals with the abstraction of that specific provider so I can seamlessly integrate it into my application.
Or better, find an already existing solution that I can just dowonload.
Ideally this abstraction service would be hosted independently from my application so it can be used for several applications and be upgraded independently.
The last of the 3 solutions above looks promising from the concept.
Everything is just ported to an synthetic OpenID, and the website jut has to implement OpenID.
After a while i found Django socialauth, a python based authentication system for the Django Webframework. But it looks like it operates as described above and i think this is the same login system that Stackoverflow uses (or at least some modified fork...).
I downloaded it and tried to set it up and to see whether it could be set up as a standalone solution but I had no luck, as I am not so into python either.
I would love a PHP based solution.
So after this long text my question precisely is:
How would you implement SSO, any better idea than porting everything and have OpenID as basis?
What are the pros and cons of that?
Do you know any already existing solutions? Preferrably open source.
I hope this question is not too subjective, thanks in advance.
Update:
I concluded that building a proxy / wrapper or what you might call it for Facebook, to port it to an OpenID so it becomes an OpenID endpoint / provider would be the best option.
So that exactly what i did.
Please see my answer below.
I added the bounty to get feedback/discussion on it. Maby my approach is not so good as i currently think it is!
As original author of this answer, I want to note that I regard it as
OUTDATED. Since most providers decided to exclusively implement Oauth instead of Openid. Newer Openid services will also likely use
openid connect, which is based on oauth. There are good libraries like for example: https://github.com/hybridauth/hybridauth
After the discussion of the already existing answer i sum up:
Almost every major provider is an openid provider / endpoint including Google, Yahoo, Aol.
Some of them requrie the user to specify the username to construct the openid endpoint.
Some of them (the ones mentioned above) do have discovery urls, where the user id is automatically returned so that the user only has to click. (i would be glad if someone could explain the technical background)
However the only pain in the ass is Facebook, because they have their Facebook connect where they use an adapted version of OAuth for authentication.
Now what I did for my project is to set up an openid provider that authenticates the user with the credentials of my facebook Application - so the user gets connected to my application - and returns a user id that looks like:
http://my-facebook-openid-proxy-subdomain.mydomain.com/?id=facebook-user-id
I also configured it to fetch email adress and name and return it as AX attributes.
So my website just has to implement opend id and i am fine :)
I build it upon the classes you can find here: http://gitorious.org/lightopenid
In my index.php file i just call it like this:
<?php
require 'LightOpenIDProvider.php';
require 'FacebookProvider.php';
$op = new FacebookProvider;
$op->appid = 148906418456860; // your facebook app id
$op->secret = 'mysecret'; // your facebook app secret
$op->baseurl = 'http://fbopenid.2xfun.com'; // needs to be allowed by facebook
$op->server();
?>
and the source code of FacebookProvider.php follows:
<?php
class FacebookProvider extends LightOpenIDProvider
{
public $appid = "";
public $appsecret = "";
public $baseurl = "";
// i have really no idea what this is for. just copied it from the example.
public $select_id = true;
function __construct() {
$this->baseurl = rtrim($this->baseurl,'/'); // no trailing slash as it will be concatenated with
// request uri wich has leading slash
parent::__construct();
# If we use select_id, we must disable it for identity pages,
# so that an RP can discover it and get proper data (i.e. without select_id)
if(isset($_GET['id'])) {
// i have really no idea what happens here. works with or without! just copied it from the example.
$this->select_id = false;
}
}
function setup($identity, $realm, $assoc_handle, $attributes)
{
// here we should check the requested attributes and adjust the scope param accordingly
// for now i just hardcoded email
$attributes = base64_encode(serialize($attributes));
$url = "https://graph.facebook.com/oauth/authorize?client_id=".$this->appid."&redirect_uri=";
$redirecturl = urlencode($this->baseurl.$_SERVER['REQUEST_URI'].'&attributes='.$attributes);
$url .= $redirecturl;
$url .= "&display=popup";
$url .= "&scope=email";
header("Location: $url");
exit();
}
function checkid($realm, &$attributes)
{
// try authenticating
$code = isset($_GET["code"]) ? $_GET["code"] : false;
if(!$code) {
// user has not authenticated yet, lets return false so setup redirects him to facebook
return false;
}
// we have the code parameter set so it looks like the user authenticated
$url = "https://graph.facebook.com/oauth/access_token?client_id=148906418456860&redirect_uri=";
$redirecturl = ($this->baseurl.$_SERVER['REQUEST_URI']);
$redirecturl = strstr($redirecturl, '&code', true);
$redirecturl = urlencode($redirecturl);
$url .= $redirecturl;
$url .= "&client_secret=".$this->secret;
$url .= "&code=".$code;
$data = $this->get_data($url);
parse_str($data,$data);
$token = $data['access_token'];
$data = $this->get_data('https://graph.facebook.com/me?access_token='.urlencode($token));
$data = json_decode($data);
$id = $data->id;
$email = $data->email;
$attribute_map = array(
'namePerson/friendly' => 'name', // we should parse the facebook link to get the nickname
'contact/email' => 'email',
);
if($id > 0) {
$requested_attributes = unserialize(base64_decode($_GET["attributes"]));
// lets be nice and return everything we can
$requested_attributes = array_merge($requested_attributes['required'],$requested_attributes['optional']);
$attributes = array();
foreach($requested_attributes as $requsted_attribute) {
if(!isset($data->{$attribute_map[$requsted_attribute]})) {
continue; // unknown attribute
}
$attributes[$requsted_attribute] = $data->{$attribute_map[$requsted_attribute]};
}
// yeah authenticated!
return $this->serverLocation . '?id=' . $id ;
}
die('login failed'); // die so we dont retry bouncing back to facebook
return false;
}
function get_data($url) {
$ch = curl_init();
$timeout = 5;
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,$timeout);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
}
Its just a first working version (quick and dirty)
Some dynamic stuff is hardcoded to my needs.
It should show how and that it can be done.
I am happy if someone picks up and improves it or re writes it or whatever :)
Well i consider this question answered
but I add a bounty just to get discussion. I would like to know what you think of my solution.
I will award the bounty to the best answer/comment beside this one.
OpenID is going to be your best bet for this application. It is supported by many, providers:
Google
Yahoo
MyOpenID
AOL
The Only problem is that twitter has not implemented OpenID yet. This is probably due to the fact that they are a proprietery based company, so they wanted their 'own' solution.
To solve that solution, you might write a wrapper class to provide compatibility with OpenID, but the chance is that even if your users don't have a twitter account, they might have a Facebook, Google, or Yahoo account.
Facebook Supports oauth, so you will have to port oauth to OpenID
Some PHP libraries for OpenID can be found here.
Now, some questions have been raised about facebook being an oauth provider.
Their oauth URL is "https://graph.facebook.com/oauth/authorize"
If you still do not belive me, then you can look at this javascript file, where I got that URL. If you don't believe that javascript file, then notice that it is hosted by stackexchange, the provider of this site. Now you must beleive that.
Fast forward two years and the answer of "OpenID is the answer" appears to be falling by the wayside by a number of the big providers. Most of the major third-party integration sites seem to have moved onto some flavor of OAuth (usually OAuth2). Also, if you don't mind NOT using OpenID/OAuth, there is a now complete SSO solution written in PHP (Disclaimer and full disclosure: This product is developed and maintained by myself under the CubicleSoft banner):
Single Sign-On Server/Client
Which didn't exist when this question was originally asked. It has a liberal license (MIT or LGPL) and meets your requirement of being an abstraction layer. The project tends to be focused toward enterprise sign ins but has some social media sign ins in the mix too (Google and Facebook).
You might also want to look at HybridAuth, which is only focused on social media sign ins but is more of a library than a prebuilt solution that you can throw onto a server and be done with it. So there is a bit more work involved with setting it up. It really depends on what you are after.
If you are happy with your OpenID solution, then great, but there are more options today than there were two years ago and people are still finding this thread.