How to request permission access from user without asking the user to insert information Google Sign-Up - perl

I have implemented google sign-in for my web app, in which I've used this library: https://metacpan.org/pod/Google::RestApi::Auth::OAuth2Client to get the code value, the access token and after that the user's information (email, name, google-id).
My problem in this implementation is that the user is prompt for consent where he needs to insert his information (email and password) and I would like to redirect the user for consent where he is able to select the accounts, and not insert his information. I don't know if my implementation, where I make the GET request, is incorrect or if it is because of the library that I use.
In front-end, I have a form which calls a route that redirects to my back-end function 'on_google_login':
sub on_google_login {
my $self = shift;
my $redirect_name = $self->get_redirect_name();
$self->session(redirect => $redirect_name);
my $google = Google::RestApi::Auth::OAuth2Client->new(
client_id => $ENV{GOOGLE_CLIENT_ID},
client_secret => $ENV{GOOGLE_SECRET},
redirect_uri => $ENV{GOOGLE_BASE_URL} . '/google_callback'
);
my $url = $google->authorize_url(
display => 'page'
);
$self->redirect_to($url);
}
And this is my callback function, where I extract the 'code' and I request the user's information using the access token.
sub on_google_callback {
my $self = shift;
my $code = $self->req->param('code');
my $google = Google::RestApi::Auth::OAuth2Client->new(
client_id => $ENV{GOOGLE_CLIENT_ID},
client_secret => $ENV{GOOGLE_SECRET},
redirect_uri => $ENV{GOOGLE_BASE_URL} . '/google_callback'
);
if (not (defined $code)) {
return $self->render(text => 'Did not connect to Google');
}
my $redirect_name = $self->session('redirect') // 'home';
delete $self->session->{'redirect'};
my $access_token = $google->access_token($code)->access_token;
my $url = $ENV{GOOGLE_ENDPOINT} . $access_token;
my $request = HTTP::Request->new(GET => $url);
my $ua = LWP::UserAgent->new();
my $info = decode_json($ua->request($request)->content);
my ($google_id, $name, $mail) = ($info->{sub}, $info->{name}, $info->{email});
if (!defined $google_id) {
return $self->render(
template => 'validation/custom_error',
title => 'Error logging in with Google',
message => 'Sorry, something went wrong when attempting to log you in ' .
'with Google. Please try again and contact us in the chat if this ' .
'persists.',
status => 400);
}
my $found_user = $self->db->resultset('User')->by_mail($mail);
if ($found_user) {
return unless validate_user_can_login($self, $found_user);
return unless set_user_data_on_login($self, $found_user);
$self->redirect_to($redirect_name);
} else {
$self->session(name => $name);
$self->session(mail => $mail);
$self->redirect_to('/register');
}
return;
}

Related

Slim 3: How to access user information from request headers

I am using Slim 3.1 and able to authenticate correctly i.e. able to generate the token and use it for another POST request. Now I want to parse the request header to extract the user information so I can identify which user have sent the request.
Here is my code to get the token.
$app->post('/login/token', function (Request $request, Response $response,
array $args) {
$input = $request->getParsedBody();
$now = new DateTime();
$future = new DateTime("+10 minutes");
$server = $request->getServerParams();
$jti = (new Base62)->encode(random_bytes(16));
$payload = [
"iat" => $now->getTimeStamp(),
"exp" => $future->getTimeStamp(),
"jti" => $jti,
"sub" => $input['username']
];
$sql = "SELECT * FROM user WHERE User_Name= :username";
$sth = $this->db->prepare($sql);
$sth->bindParam("username", $input['username']);
$sth->execute();
$user = $sth->fetchObject();
// verify email address.
if(!$user) {
return $this->response->withJson(['error' => true, 'message' => 'These credentials do not match our records.']);
}
// verify password.
if (!password_verify($input['password'],$user->User_Password)) {
return $this->response->withJson(['error' => true, 'message' => 'These credentials do not match our records.']);
}
$settings = $this->get('settings'); // get settings array.
//$token = JWT::encode(['User_ID' => $user->User_ID, 'username' => $user->User_Name], $settings['jwt']['secret'], "HS256");
$token = JWT::encode($payload, $settings['jwt']['secret'], "HS256");
return $this->response->withJson(['token' => $token, 'ACL' => $user->User_ACL]);
});
This returns me a token that I send in the following POST request
$app->group('/api', function(\Slim\App $app) {
$app->post('/createuser', function (Request $request, Response $response,
array $args) {
$headerValueArray = $request->getHeader('HTTP_AUTHORIZATION');
return $this->response->withJson(['success' => true, $token]);
});
});
The above POST request gives the following output
{
"success": true,
"0": ["Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MzU4Mjk0OTUsImV4cCI6MTUzNTgzNjY5NSwianRpIjoiMWc5ZFM3dUNLbzl1blRQZzBmYjU2diIsInN1YiI6InN5c2FkbWluIn0.vo3FBPhBkhfA2y7AG-afmjfeEhygIYY7lIaaVNX5i5k"]
}
I need to parse this token to extract the user information to see if its the valid user to perform this operation.In other words, how I can decode the above token.
Any help here will be much appreciated!

Perl wrapper around Facebook OAuth v2.0 protocol

I try to get some details from user of app, but have problem because don't know what to do with code I get after I run this code below, I get something like this:
http://myapp.com/?code=AQAPYR33UZrJpuPrAfE_lL3XPCBaw1ZcTLRl_9cYU2H77T7S3VKVz0njBt_0Jff54HfrzHW9_r-WF_-GN1VWHjEi9zNIMHZHFSEqXcTD7vbP2HKqN1DaEAealNxBNpATFb-RAFYIAZL4EqSUXLCYJ9-WvvDF5oWCUd5vAQ3TiXgPUDWml35v43fBn2xXJo1
I thought its access token but it didn't...
use CGI;
my $cgi = CGI->new;
use Net::Facebook::Oauth2;
my $fb = Net::Facebook::Oauth2->new(
application_id => 'your_application_id',
application_secret => 'your_application_secret',
callback => 'myapp.com'
);
###get authorization URL for your application
my $url = $fb->get_authorization_url(
scope => ['offline_access','publish_stream'],
display => 'page'
);
####now redirect to this url
print $cgi->redirect($url);
##once user authorizes your application facebook will send him/her back to your application
##to the callback link provided above
###in your callback block capture verifier code and get access_token
my $fb = Net::Facebook::Oauth2->new(
application_id => 'your_application_id',
application_secret => 'your_application_secret',
callback => 'http://myapp.com'
);
my $access_token = $fb->get_access_token(code => $cgi->param('code'));
###save this token in database or session
##later on your application you can use this verifier code to comunicate
##with facebook on behalf of this user
my $fb = Net::Facebook::Oauth2->new(
access_token => $access_token
);
my $info = $fb->get(
'https://graph.facebook.com/me' ##Facebook API URL
);
print $info->as_json;

how can I get a facebook Page access token from a users access token using php?

I am trying to get a page access token starting out with just a users access token stored in my database and a page id. So far I have not been using the facebook.php instead just using php's curl_* functions. So far I can send posts to the page (with a hard coded page id) but I want to impersonate the page when doing so.
Can I do this easily without facebook.php, that would be nice as it might save me from feeling like I should rewrite what I've done so far. If not, then how would I get the page access token from the facebook object - remember so far at least I don't store user ids or page ids in my db, just user access tokens and of course my app id and secret.
I've been looking at the example for getting page access tokens but I find it not quite what I need as it gets a user object and in so doing seems to force the user to login to facebook each time, but I stored the user access token to avoid exactly that from happening.
Do I need more permissions than manage_page and publish_stream? I tried adding offline_access but it doesn't seem available anymore (roadmap mentions this).
here is some of my code from my most recent attempt which uses the facebook.php file:
// try using facebook.php
require_once 'src/facebook.php';
// Create our Application instance
$facebook = new Facebook(array(
'appId' => $FB_APP_ID, // $FB_APP_ID hardcoded earlier
'secret' => $FB_APP_SECRET, // $FB_APP_SECRET hardcoded earlier
));
$facebook->setAccessToken($FB_ACCESS_TOKEN );
//got user access token $FB_ACCESS_TOKEN from database
// Get User ID -- why?
$user = $facebook->getUser();
//------ get PAGE access token
$attachment_1 = array(
'access_token' => $FB_ACCESS_TOKEN
);
$result = $facebook->api("/me/accounts", $attachment_1);
foreach($result["data"] as $page) {
if($page["id"] == $page_id) {// $page_id hardcoded earlier
$page_access_token = $page["access_token"];
break;
}
}
echo '<br/>'.__FILE__.' '.__FUNCTION__.' '.__LINE__.' $result= ' ;
var_dump($result); //this prints: array(1) { ["data"]=> array(0) { } }
$facebook->setAccessToken($page_access_token );
// Get User ID, why - re-init with new token maybe?
$user = $facebook->getUser();
//------ write to page wall
try {
$attachment = array(
'access_token' => $page_access_token,
'link' => $postLink,
'message'=> $postMessage
);
$result = $facebook->api('/me/feed','POST', $attachment);
echo '<br/>'.__FILE__.' '.__FUNCTION__.' '.__LINE__.' $result= ' ;
var_dump($result);
} catch(Exception $e) {
echo '<br/>'.__FILE__.' '.__FUNCTION__.' '.__LINE__.' $e= ' ;
var_dump($e); /*this gives : "An active access token must
be used to query information about the
current user." */
}
die;
Thanks
PS: I hardcoded the user id and started calling
$result = $facebook->api("/$user_id/accounts", $attachment_1);
and I still get an empty result.
PPS: The Graph API Explorer does not show my fan pages either even though my account is set as the Manager. My attempts to post work but show as being from my account rather than from the page.
PPPS: made a little progress by adding permissions on the graph explorer page to get an access token that way but that doesn't help as I need to the the access token programmatically. When a user with many fan pages logs in to my site I want to show them the list of their facebook fan pages to choose from. In practice aren't the permissions just granted on the app?
PPPPS: the list of permissions on my app now stands at : email, user_about_me, publish_actions
and
Extended Permissions:
manage_pages, publish_stream, create_note, status_update, share_item
do I need more? when I try now I still fail to get anything from the call to:
$facebook->api("/$user_id/accounts", $attachment_1);
Px5S: DOH!!! I see now that I was neglecting to add the manage_pages permissions to my call for a user access token when my scripts first get one and store it in the DB. But when I reuse that new access token I still get the error : "An active access token must be used to query information about the current user." So, can't such tokens be reused? Aren't they long term? will read more stuff...
Here is my functioning code, still messy but seems to work, note the scopes on the first $dialog_url, and please feel free to mock my code or even suggest improvements :
function doWallPost($postName='',$postMessage='',$postLink='',$postCaption='',$postDescription=''){
global $FB_APP_ID, $FB_APP_SECRET;
$APP_RETURN_URL=((substr($_SERVER['SERVER_PROTOCOL'],0,4)=="HTTP")?"http://":"https://").$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'].'?returnurl=1';
$code = $_REQUEST["code"];
$FB_ACCESS_TOKEN = getFaceBookAccessToken( );
$FB_ACCESS_TOKEN_OLD = $FB_ACCESS_TOKEN;
//if no code ot facebook access token get one
if( empty($code) && empty($FB_ACCESS_TOKEN) && $_REQUEST["returnurl"] != '1')
{
// if( $_REQUEST["returnurl"] == '1') die;
$dialog_url = "http://www.facebook.com/dialog/oauth?client_id=".$FB_APP_ID."&redirect_uri=".$APP_RETURN_URL."&scope=publish_stream,manage_pages";
header("Location:$dialog_url");
}
if( empty($FB_ACCESS_TOKEN) ){
if($_REQUEST['error_code'] == '200'){
return null;
}else if (!empty($code)){
$token_url = "https://graph.facebook.com/oauth/access_token?client_id=".$FB_APP_ID."&redirect_uri=".urlencode($APP_RETURN_URL)."&client_secret=".$FB_APP_SECRET."&code=".$code;
$access_token = file_get_contents($token_url);
$param1=explode("&",$access_token);
$param2=explode("=",$param1[0]);
$FB_ACCESS_TOKEN=$param2[1];
}else{
return null;
}
}
if(!empty($FB_ACCESS_TOKEN) && $FB_ACCESS_TOKEN_OLD != $FB_ACCESS_TOKEN) {
setFaceBookAccessToken( $FB_ACCESS_TOKEN);
}
$_SESSION['FB_ACCESS_TOKEN'] = $FB_ACCESS_TOKEN;
$page_name = '';
$page_id = getFaceBookPageId(); //from db
if(empty($page_id ) ) return null;
//in case there are multiple page_ids separated by commas
if(stripos($page_id, ',') !== false ){
$page_ids = explode(',', $page_id) ;// = substr($page_id, 0, stripos($page_id, ','));
}
$result = null;
foreach($page_ids as $page_id){
$page_id = trim($page_id);
if( !empty($FB_ACCESS_TOKEN)){
//get page_id
require_once 'src/facebook.php';
// Create our Application instance (replace this with your appId and secret).
$facebook = new Facebook(array(
'appId' => $FB_APP_ID,
'secret' => $FB_APP_SECRET
));
$facebook->setAccessToken($FB_ACCESS_TOKEN );
//------ get PAGE access token
$page_access_token ='';
$attachment_1 = array(
'access_token' => $FB_ACCESS_TOKEN
);
$result = $facebook->api("/me/accounts", $attachment_1);
if(count($result["data"])==0) {
return null;
}
foreach($result["data"] as $page) {
if($page["id"] == $page_id) {
$page_access_token = $page["access_token"];
break;
}
}
//------ write to page wall
try {
$attachment = array(
'access_token' => $page_access_token,
'link' => $postLink,
'message'=> $postMessage
);
$result = $facebook->api('/me/feed','POST', $attachment);
} catch(Exception $e) {
return null;
}
} //end if( !empty($FB_ACCESS_TOKEN))
}//end foreach
return $result; }
Now, I wonder if I can send the same message to several pages at once ...
Yup, just by looping over the ids, see above, it now supports multiple page ids.
And unless someone wants to contribute to the code - there's lots of ways it can be improved - I'm done.

Facebook OAuth access_token error

Full code.
public function indexAction(){
echo 'Login met facebook ';
}
const FB_GRAPH_URL = "https://graph.facebook.com/";
public function fbloginAction() {
$fbCode = $this->_getParam("code");
$getStr = self::FB_GRAPH_URL. 'oauth/access_token?' .http_build_query(array(
'client_id' => 'APP_ID',
'type' => 'client_cred',
'client_secret' => 'SECRET_KEY',
'code' => $fbCode)
);
$accessToken = file_get_contents( $getStr );
krumo($accessToken) ;
$dbpath = "https://graph.facebook.com/me?$accessToken" ;
$cont = file_get_contents($dbpath ) ;
krumo($cont);
}
When i try make GET query to Facebook.
$dbpath = "https://graph.facebook.com/me?$accessToken" ;
$cont = file_get_contents($dbpath ) ;
I receive the error :
failed to open stream: HTTP request failed! HTTP/1.0 400 Bad Request
in /home.....
When paste manually the $dbpath value(path) to web browser, i got next error:
{
"error": {
"message": "An active access token must be used to query information about the current user.",
"type": "OAuthException",
"code": 2500
}
}
How fix that error?
You probably want to use the Server-Side Authentication flow. By checking the calls in the documentation it is quite clear, which of your calls are wrong.
First, your call to the oauth/access_token endpoint takes no argument 'type' => 'client_cred', but it needs the parameter for your redirect_uri again:
$getStr = self::FB_GRAPH_URL . 'oauth/access_token?' . http_build_query(array(
'client_id' => 'APP_ID',
'redirect_uri' => 'REDIRECT_URI',
'client_secret' => 'SECRET_KEY',
'code' => $fbCode)
);
Then, you can't just take the answer of this call as your access_token, as there is much more in it:
access_token=USER_ACCESS_TOKEN&expires=NUMBER_OF_SECONDS_UNTIL_TOKEN_EXPIRES
and you only want the access_token part of it:
$response = file_get_contents($getStr);
$params = null;
parse_str($response, $params);
$dbpath = "https://graph.facebook.com/me?access_token=" . $params['access_token'];

Facebook connect works fine but troubles inside Facebook iframe

I actually tried to find a solution but nothing and I hope I will find a solution here !
I use facebook connect in my website, everything works fine
when I am logged on facebook, I am automatically logged into my site through facebook connect, and vice versa.
Now i tried to make it a facebook application but I have the following problem:
I try to run inside the facebook iframe but unfortunately I'm not automatically logged in. sometimes it works, sometimes nothing happens , Sometimes I get this message: (Could not get the Facebook session. Your server may not Be Able Securely Facebook to connect to the user to retrieve information.)
some code im using :
include 'includes/facebook.php';
$facebook = new Facebook(array(
'appId' => $setting['facebook_appid'],
'secret' => $setting['facebook_secret'],
'cookie' => true, // enable optional cookie support
));
if ($facebook->getUser()) {
$facebook_session = 1;
try {
$fb_user = $facebook->api('/me');
} catch (FacebookApiException $e) {
error_log($e);
}
function get_facebook_cookie($app_id, $application_secret) {
$args = array();
parse_str(trim($_COOKIE['fbs_' . $app_id], '\\"'), $args);
ksort($args);
$payload = '';
foreach ($args as $key => $value) {
if ($key != 'sig') {
$payload .= $key . '=' . $value;
}
}
if (md5($payload . $application_secret) != $args['sig']) {
return null;
}
return $args;
}
$cookie = get_facebook_cookie($setting['facebook_appid'], $setting['facebook_secret']);
$open = #file_get_contents('https://graph.facebook.com/me?access_token='.$cookie['access_token']);
if ($open != FALSE) {
$fbdata = json_decode($open);
$fb_user = array();
foreach($fbdata as $key => $fbdata2) {
$fb_user[$key] = $fbdata2;
}
}
}
else {
$facebook_session = 0;
}
ps: im using of course a Secure Canvas URL
Please help !
Thanks in advance
My Facebook Connect login worked fine, but not inside the Facebook Canvas/iFrame it didn't work at all. I had to change my login method (now that facebook users have to be able to autologin in your application) and what basically worked for me was this: How to authorize Facebook app using redirect in canvas?
This is what I had before (I use CakePHP):
$params = array(
'scope' => 'email'
);
$loginUrl = $facebook->getLoginUrl($params);
$this->redirect($loginUrl);
And I changed it to this which worked quite well:
$loginUrl = $facebook->getLoginUrl(
array(
'canvas' => 1,
'fbconnect' => 0,
'req_perms' => 'publish_stream'
)
);
echo '<script>top.location="'.$loginUrl.'";</script>';
I'm not sure if that really helps you as your app seems a bit more complicated than mine, but maybe you can solve your issue with this answer or the post I linked to.