I am developing a PHP REST api using Api Platform for my website where users can post 'Reviews' (similar to articles).I want to track unique page views for this articles so I can later sort by most viewed reviews and display them.
I have read a lot of solutions online and I have implemented two of them: Google Analytics tracking and Redis + browser fingerprinting.
First, the Google Analytics tracking works quite well, I am using the 'ga:uniquePageviews' property with a filter for the page path to get using the Google PHP Api the unique page views. The only problem with this method is that some users use ad blockers and they block Google Analytics so no page view will be recorded for those people.
I have also implemented view tracking using Redis + browser fingerprinting (using fingerprintjs2) for anonymous users, and using the user id for logged in users. So for logged in users, if they have not visited the page in the last 30 minutes, a new unique view is added. This seems to work quite well.
The problem is with anonymous users. Currently, on the website side I am using fingerprintjs2 library to generate a browser fingerprint which is then passed to the REST api in a header and if that fingerprint did not visit the page in the last 30 minutes, then a new unique view is added. But because my REST api is going to be public (I plan on maybe also making a mobile app in the future), anyone can just put whatever then want in the fingerprint header and just keep changing it and a new unique view will be recorded and I am not sure how to fix this. Would allowing CORS only from my website domain fix this?But then what about the mobile app?
Right now I am inclined to think that using Google Analytics is the best way despite ad blockers, but the Redis approach just seems pretty cool to me and I would also like to get that working.
Here is a code example of what I am currently using for Redis:
public function addAndGetReviewViews(Request $request, $id)
{
$viewsKey = 'pageviews-review-' . $id;
$setKey = 'uniqueviews-review-' . $id;
$user = $this->getUser();
if ($user) {
$userId = $user->getId();
} else if ($request->headers->has('X-Anonymous-Fingerprint')) {
$userId = base64_encode(
substr($request->headers->get('X-Anonymous-Fingerprint'), 0, 32)
);
} else {
// Only add views if user is logged in or has anonymous fingerprint
return $this->redis->get($viewsKey) ?: 0;
}
$setExists = $this->redis->exists($setKey);
if (!$this->redis->sismember($setKey, $userId)) {
$this->redis->sadd($setKey, $userId);
$this->redis->incr($viewsKey);
}
if (!$setExists) {
$this->redis->expire($setKey, 1800);
}
return $this->redis->get($viewsKey);
}
Related
I'm developing a mobile app which has to access to an external webapp (PHP + Codeigniter) to administrate the actions queried by ajax.
So by this way, there is a problem. If anyone see the urls used, could delete rows, or modify the user's info from the database. So I thought in this system to aboid this:
After a sucessful login I would do this:
// getToken : https://stackoverflow.com/a/13733588/2154101
$this->session->set_userdata('private_token', getToken(50));
$public_token = getToken(50);
$this->session->set_userdata('secure_token', md5("$private_token:$public_token"));
$data['token'] = $public_token;
// some stuff ...
// send $data in JSON
Then the client would the public token in the next query I would do this on the server:
$public_token = $this->input->post('token');
$data['token'] = get_public_token($public_token);
// some stuff ...
// send $data in JSON
Where get_public_token is within a helper with this code:
public get_public_token($public_token) {
$last_secure_token = $this->session->userdata('secure_token');
$private_token = $this->session->userdata('private_token');
$actual_token = md5("$private_token:$public_token");
if ($actual_token === $last_secure_token) {
$public_token = getToken(50);
$this->session->set_data('private_token', getToken(50));
$this->session->set_data('secure_token', md5("$private_token:$public_token"));
return $public_token;
} else { // you are cheating me ...
$this->session->sess_destroy();
redirect('/');
}
}
So only the user of this session could modify the data of the database.
I'm just trying to do the same explained here: https://stackoverflow.com/a/17371101/2154101
The session are encrypted, and I store them in a database too.
Do you think this method will work ok? Am I missing something important?
You should create an API for your mobile application. Create a authentication mechanism.
If your database holds user specific data, then you should create account for each user. So if the user sniffs the network and tries to call the api manually, then he could only change he's own data.
There are some API libraries for php out there, you should look into that.
Actually your solution is doing more than necessary. The only token of interest is the public_token sent back and forth. So you can throw away private_token and secure_token from session data, keeping only public_token for checking. Your current check is something like (X + 5)/2 == (14 + 5)/2 (is [received_token + 5]/2 equal to [14 + 5]/2 ?) when you can simplify to X == 14.
However if someone is sniffing the network, he can get the last token sent to a client and use it to hijack into that session. He can execute anything while the original client doesn't send a request with the outdated token, killing the session.
A better solution would be creating a secure_key after login and keep it at both ends (client and server). Then server would keep sending a new public_token at each response, but the client would send a md5(secure_key + public_token) at requests. This would narrow even more the hijacking window to the exact point where the session started. Without the original key, attackers can't create a valid md5.
However we are talking about minor hacking fans here. Anyone more zealous could hack that anyway. If you are concerned about that, then throw away all that stuff and simply use a HTTPS connection. With a trusted connection your sessions and access control rules are protected.
The better way is create API using SOAP or SAML2.
OAuth can be a very good solution: http://oauth.net/. It takes care of token and has a very secured API! If you wish to support secure authentication of web application + mobile application then it can be a good/proven solution!
On the other hand, it really depends on how complex your current system is and how the system is going to be in future.
I am developing a Facebook app which will need to read the posts of all the
users who have authenticated the app. The "posts reading script" will run through corn
and will get all the posts from the users no matter they are online or offline. I have been
trying to solve this problem for 1 week but with no fruitful results. here is my code
function index()
{
$this->load->model("proofreader"); //my scanner
$this->load->library('email'); //library for sending emails
$this->facebook->setExtendedAccessToken($this->facebook->getAccessToken());
$i = 0;
foreach ($users as $rows) //$users is an array containing data from users
{
$i++;
$posts = $this->facebook->api('/' . $rows[$i] . '/posts');
$this->proofreader->read($posts);
if ($this->proofreader->returnresults()) { /*do appropriate functions*/
}
}
}
All i want to do it is this:
A way to read all the posts of a user who has authenticated the app and is offline.
Please correct me what's wrong with the above script or if you can, direct me to any alternative. I have read the Facebook documentations more than a 1000 times last week.
Thanks in Advance
I got it. I need an extended access token to access user's posts while he/she is offline. Else , It will return only the actions of the users and not the messages. furthermore, i need to ask for read_stream permissions while authenticating the app
I've already got a database set up with a table that is successfully populated with the final (permanent?) OAuth User Token and OAuth User Secret. The thing I don't understand is how I'm supposed to know what the current user's ID is, especially when it's been 2 weeks since their last login. My app is authorized by all of its users, so theoretically Twitter can look at the list of authorized apps for the current user and share the Twitter User ID, right? Isn't there some good way of requesting (on behalf of the current user) what his ID is? I feel like the temporary tokens should be able to facilitate this somehow... If it helps, every user in my app is just a Twitter account with some extra info. I'm just looking for the best way to utilize the tokens and secrets that are in my database...
I'm using PHP (libraries: Codebird-PHP & tmhOAuth) so if you could show an example in PHP that'd be nice, but really I just want to know how I'm supposed to use this information that I'm storing.
Thanks!
I'm assuming you store the data together with some username or user id that identifies the users of your website and links them to their proper twitter id. In order to get the basic info of your user, after authorization, you have to use the endpoint https://api.twitter.com/1.1/account/verify_credentials.json with a GET.
The documentation for the 1.1 API can be found here.
This returns an array. You find the username uder "screen_name" and the user id under "id" or "id_string".
The question is a possible duplicate of Get current user's info from Twitter API, but I've added an answer because that discussion points to the deprecated API. The code you find there, nevertheless, is still useful (it appears to use Abraham William's library, but the steps are basically the same). Replace the classes and functions with those you have in Matt Harris' library. I don't know codebird, sorry!
EDIT: I am also providing a code sample (tested and working, although I have issues with tmhOAuth, so I use it occasionally only for myself. I have noticed that, when I try to post, it sometimes returns some weird error codes and I can't figure out why):
// Authentication page, with button. You have already connected to your database
$mywebsiteuser = $_SESSION['website_user_id'];
$query= "SELECT * FROM `table_where_you_store_twitter` WHERE website_user_id ='$mywebsiteuser'";
$sql= $mysqli->query($query) or die($mysqli->error.__LINE__); // or whatever else to check is the query fails.
if ($sql->num_rows != 0){
//etc. retrieve data and set the sessions.
// already got some credentials stored?
if ( isset($_SESSION['access_token']) ) {
$tmhOAuth->config['user_token'] = $_SESSION['access_token']['oauth_token'];
$tmhOAuth->config['user_secret'] = $_SESSION['access_token']['oauth_token_secret'];
$code = $tmhOAuth->request('GET', $tmhOAuth->url('1/account/verify_credentials'));
if ($code == 200) {
$resp = json_decode($tmhOAuth->response['response']);
echo $resp->screen_name;
echo $resp->id;
//Etc. Instead of printing them you it's a good idea to store them in the db.
} else {
outputError($tmhOAuth);
}
// we're being called back by Twitter
} elseif (isset($_REQUEST['oauth_verifier'])) {
$tmhOAuth->config['user_token'] = $_SESSION['oauth']['oauth_token'];
$tmhOAuth->config['user_secret'] = $_SESSION['oauth']['oauth_token_secret'];
$code = $tmhOAuth->request('POST', $tmhOAuth->url('oauth/access_token', ''), array(
'oauth_verifier' => $_REQUEST['oauth_verifier']
));
if ($code == 200) {
//etc.
Anyhow, all in all, in order to get the info of a user you need them to authorize your app first. I check if I have something from my user with the user's session variables on my website, not through twitter. If I have nothing stored, I ask them to authorize the app. I hope this helps.
Access Token : 1274865264-QiVY50RGnmJz6AU9IPRxxiXfv4DYqo0nj6wg8hS
Access Token Secret : fZQnHSuSpwARicIdLqkqQLy1JeG9LxrbNIRKypWcGR
First part of Access Token is user id
I have been implementing a Google/Facebook authentication system on my site. I use their respective service to authenticate the user, then use the results to instantiate sessions on my end. The Google one is running no problem, but for the Facebook one I have hit a snag.
I can authenticate users with no problem, but when trying to authenticate a company or other Facebook page type, it fails.
For instance, using the GraphAPI, I am able to retrieve the values shown in one of their examples https://graph.facebook.com/btaylor and store it properly.
But when I try to use the same system to authenticate a company ( https://graph.facebook.com/nike for instance), it fails since the objects are not part of the user results returned by the GraphAPI.
Normal stuff setting up the call:
$FBcookie = get_facebook_cookie(YOUR_APP_ID, YOUR_APP_SECRET);
$user = json_decode(file_get_contents_curl('https://graph.facebook.com/me?access_token='.$FBcookie['access_token']));
And when storing results, it works fine as a user:
$_Fname =$user->first_name;
$_Lname =$user->last_name;
But trying to access the expected attributes seen at the Nike example above, nothing gets returned.
I even tried echoing out the object to see whats there:
print('<pre>');
print_r($user);
print('</pre>');
For the user, I see everything, for non-user (company, event or other page type), I see nothing.
Question is, has anyone seen what other objects we may need to query via the GraphAPI? The Facebook developer pages are all over the place and have no real concise tutorials on this.
Any help would be much appreciated ;)
Company/Page info on facebook is as follows:
$user->id;
$user->name;
$user->picture;
$user->link;
$user->likes;
$user->category;
$user->website;
$user->username;
$user->description;
$user->public_transit;
So $user->first_name; and $user->last_name; don't exist.
You could try something like this :
if(empty($user->first_name)&&empty($user->last_name)){
//probably company set $user->username;
}
I'm trying to issue a Facebook Graphp API search call for groups with a specific search term. The important fact is to search for groups not only beginning with the term but also to search for the term within the group name. So something like that.
FQL => SELECT * FROM groups WHERE groupname LIKE '%term%'
As far as i know this isn't possible in FQL. Therefore I have to use the Graph API search.
http://developers.facebook.com/docs/api#search
But I have to issue th call even if the user isn't logged in. Is this possible
or is there a possibility to log in a default user with some curl calls without user interaction (without displaying a form)?
If there is a simplier solution (for instance with FQL) please tell me.
I work with the graph API and not with the FQL so I'm not 100% sure on the differences etc etc... however a way I'd try is using the cURL script at http://developers.facebook.com/docs/authentication/#authenticating-users-in-a-web-application to get an auth token, then you must add the received auth token to the graph parameters as you can see on the site (a good way to test in the graph api is to click on the example likes or queries given while logged into facebook then copying the api code and breaking up the process like that [testing that you can get results you want from the query, testing that you can get your auth token then combining] so that you know what parts are 'problem' parts).
If that fails, scroll down to the "single sign-on with java-script" and have a look at the code they use to get the auth token from the facebook cookie, I notced you may be able to access that cookie from FQL.
I hope this was of some help! Please tell me if those ideas didn't work.
Jon
Using PHP-SDK 3.1.1, no auth or access_token needed. This sample assumes you have PHP-SDK installed on the page.
Use a form post the question to the page, arguments for get and post
are included. This will return the array from search https://graph.facebook.com/search?q=facebook&type=group
<?php
$q = urlencode($_GET['qs']);
if(!$_GET['qs']){
$q = urlencode($_POST['qs']);
if(!$_POST['qs']){
$q = "facebook";
}
}
$MEsearch = $facebook->api('/search?q='.$q.'&type=group');
foreach ($MEsearch as $key=>$value) {
$i=1;
foreach ($value as $fkey=>$fvalue) {
$i++;
if($fvalue[id]=="h"){
}else{
$groupname = $fvalue[name];
$groupid = $fvalue[id];
$groupversion = $fvalue[version];
echo $groupname. '<br />';
echo $groupid. '<br />';
echo $groupversion. '<br /><hr />';
}
};
};
?>
Sample Usage http://shawnsspace.com/plugins/photofeed.php Click Get Plugin at the bottom and use the search box to see this sample in action.