I have a Laravel 8 API project using the tymon/jwt-auth package, and I'm trying to do the following.
As a user 'admin', I can delete others user's clients.
In the database, the entities are never deleted completely; I use a status attribute 'active'|'deleted' to soft-delete the entity.
After deleting a user, I need to invalidate the token if the user has one.
The problem comes with the last point, I can get the deleted user token, but I couldn't find how to invalidate that specific token in the JWT docs. I couldn't find a question like this either.
public function delete($id)
{
$parameters = ['id' => $id, 'status' => 'deleted'];
$user = request()->user();
$data = User::where('id', '<>', $user->id)->where('status', 'active')
->where('id', $parameters['id'])->first();
if (!isset($data)) {
return response()->json(['error' => 'User does not exist'], 400);
}
$data->fill($parameters)->save();
$token = auth()->tokenById($data->id);
if ($token) {
// TODO invalidate deleted user token
// The next line invalidates the current auth token, not the target
// user token auth()->invalidate($token);
}
return response()->json(['message' => 'Success'], 200);
}
If I properly delete the user from DB, the next requests using his token are now invalid because the user doesn't exist anymore. So there is a way to tell tymon/jwt-auth or the auth guard they need to check if the user has status = 'deleted'?
You can invalidate token from middleware.
if ($user->blocked) {
$token = $request->bearerToken();
\Illuminate\Support\Facades\Auth::setToken($token)->invalidate();
throw new UnauthorizedException();
}
Related
I'm currently trying to pull the list of company pages a user is the admin of with the LinkedIn API and am getting the following response:
Array
(
[response] => {
"errorCode": 0,
"message": "Member does not have permission to get companies as admin.",
"requestId": "R1LHP32UKD",
"status": 403,
"timestamp": 1482357250945
}
[http_code] => 403
)
The call works perfectly when authenticated as the same user in the LinkedIn API Console.
Has anyone else come across this?
I figured out the problem.
Even though I had made sure the app had the rw_company_admin permission checked off, I wasn't passing that in the scope when requesting oath2 authorization.
My fixed code below:
The call:
return $linkedin->getAuthorizationUrl("r_basicprofile w_share rw_company_admin", $thisurl);
The function:
public function getAuthorizationUrl($scope, $callback)
{
$params = array('response_type' => 'code',
'client_id' => $this->api_key,
'scope' => $scope,
'state' => uniqid('', true), // unique long string
'redirect_uri' => $callback,
);
// Authentication request
$url = 'https://www.linkedin.com/uas/oauth2/authorization?' . http_build_query($params);
// Needed to identify request when it returns to us
$_SESSION['Linkedin_state'] = $params['state'];
$_SESSION['Linkedin_callback'] = $callback;
// this is where to send the user
return $url;
}
First you have to get user token of linkedin user
Use Socialite or REST API package for oauth.
Once you having user token it is pretty simple to fetch Company list under admin user
$token = 'uflefjefheilhfbwrfliehbwfeklfw'; // user token
$api_url = "https://api.linkedin.com/v1/companies?oauth2_access_token=".$token."&format=json&is-company-admin=true";
$pages = json_decode(file_get_contents($api_url));
You have get $pages json array of list of company profile.
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.
I want my application to publish photos, user has already registered my app with publish_stream permission and is currently disconnected from facebook.
Documentation https://developers.facebook.com/docs/publishing/ said :
To publish a 'photo' object you need
a valid access token
publish_stream permission
I tried a HTTP POST request :
https://graph.facebook.com/<USER_ID>/photos?access_token=<APPLICATION_ACCESS_TOKEN>
POST param : "url":"<link_to_some_picture>"
And i got an exception :
content={"error":{"message":"A user access token is required to request this resource.","type":"OAuthException","code":102}}
To publish photos on behalf of user i cannot pass a user access token... Why i can post links but not photos with my application access token?
Your title states that you are using an application access token - the error you are getting clearly states the problem. You need a user access token.
You'll need to extend your users access token in order to perform actions on his/her behalf when they are not connected to Facebook.
Here is a good resource for information about dealing with expired tokens and how to refresh them -
https://developers.facebook.com/docs/authentication/access-token-expiration/
When user registered your application then you can store the user's user access token after you can used that access token to post on behalf of user.When user come to your application next time you can update that access token.
You can use following function to get extended access token:
public function getExtendedAccessToken(){
try {
// need to circumvent json_decode by calling _oauthRequest
// directly, since response isn't JSON format.
$access_token_response =
$this->_oauthRequest(
$this->getUrl('graph', '/oauth/access_token'),
$params = array( 'client_id' => $this->getAppId(),
'client_secret' => $this->getApiSecret(),
'grant_type'=>'fb_exchange_token',
'fb_exchange_token'=>$this->getAccessToken(),
));
} catch (FacebookApiException $e) {
// most likely that user very recently revoked authorization.
// In any event, we don't have an access token, so say so.
return false;
}
if (empty($access_token_response)) {
return false;
}
$response_params = array();
parse_str($access_token_response, $response_params);
if (!isset($response_params['access_token'])) {
return false;
}
return $response_params['access_token'];
}
$fid = $row[1];
echo "fid = $fid\n";
$message =$row[2];
echo "msg = $message\n";
$friends = $facebook->api("/$user/friends?fields=id");
$args= array(
'app_id' => $facebook->getAppId(),
'to' => $fid,
'link' => 'http://www.facebook.com',
'message'=>$message,
);
$post_id = $facebook->api('/fid /feed', 'POST', $args);
var_dump($post_id);
I am using Zend Gmail Oauth 1.0 for implementing login with Gmail feature.
After successful authentication, how can I get authenticated user's profile, specifically user's unique gmail id? Here is the code:
$THREE_LEGGED_SCOPES = array('https://mail.google.com/',
'https://www.google.com/m8/feeds');
$options = array(
'requestScheme' => Zend_Oauth::REQUEST_SCHEME_HEADER,
'version' => '1.0',
'consumerKey' => $THREE_LEGGED_CONSUMER_KEY,
'consumerSecret' => $THREE_LEGGED_CONSUMER_SECRET_HMAC,
'callbackUrl' => getCurrentUrl(),
'requestTokenUrl' => 'https://www.google.com/accounts/OAuthGetRequestToken',
'userAuthorizationUrl' => 'https://www.google.com/accounts/OAuthAuthorizeToken',
'accessTokenUrl' => 'https://www.google.com/accounts/OAuthGetAccessToken'
);
if ($THREE_LEGGED_SIGNATURE_METHOD == 'RSA-SHA1') {
$options['signatureMethod'] = 'RSA-SHA1';
$options['consumerSecret'] = new Zend_Crypt_Rsa_Key_Private(
file_get_contents(realpath($THREE_LEGGED_RSA_PRIVATE_KEY)));
} else {
$options['signatureMethod'] = 'HMAC-SHA1';
$options['consumerSecret'] = $THREE_LEGGED_CONSUMER_SECRET_HMAC;
}
$consumer = new Zend_Oauth_Consumer($options);
/**
* When using HMAC-SHA1, you need to persist the request token in some way.
* This is because you'll need the request token's token secret when upgrading
* to an access token later on. The example below saves the token object
* as a session variable.
*/
if (!isset($_SESSION['ACCESS_TOKEN'])) {
if (!isset($_SESSION['REQUEST_TOKEN'])) {
// Get Request Token and redirect to Google
$_SESSION['REQUEST_TOKEN'] = serialize($consumer->getRequestToken(array('scope' => implode(' ', $THREE_LEGGED_SCOPES))));
$consumer->redirect();
} else {
// Have Request Token already, Get Access Token
$_SESSION['ACCESS_TOKEN'] = serialize($consumer->getAccessToken($_GET, unserialize($_SESSION['REQUEST_TOKEN'])));
header('Location: ' . getCurrentUrl(false));
exit;
}
} else {
// Retrieve mail using Access Token
$accessToken = unserialize($_SESSION['ACCESS_TOKEN']);
}
near as I can tell you can't.
Gmail doesn't have an api just a read only feed.
However if you want that feed the scope url is:
https://mail.google.com/mail/feed/atom/
There are some api's for working with gmail accounts in the context of Google Apps.
Facebook test users (created for an app) are working different in some ways than normal users. Ie. they can't like fanpages, so you can't test if "create.edge" listener is setup correctly.
Is there a way to detect if user authenticated to app is test user? It would be useful to implement fake version of dialogs, that test user can't use (ie. liking a fanpage).
My research:
I have checked signed_request passed to app and it looks same for test users as for normal users.
I have checked graph.facebook.com/[test user id] and it also look normal.
You cannot check if user is a test user basing on his Graph API details. Similarly, you cannot check if user like’s a specific page. You can however check your page_id against user liked pages list. So in order to determine if given user_id is a regular or a test user, you’d have to check it against the application test user list:
https://graph.facebook.com/APP_ID/accounts/test-users?access_token=APP_ID|APP_SECRET
So the flow would be like this:
Authenticate user
Get his ./likes list
Find your page_id and exit successful
Get your APP ./accounts/test-users list
Find given user_id and exit successful
Exit with failure -- require user to like your page.
That’s one extra call, but you can safely cache results for performance gains, since test users cannot be converted to regular users. I’d advice to cache the "likes" check for some time as well, but YMMV.
Is Facebook user is test you can change name and password, else return error code (#200) You do not have sufficient permissions to perform this action
**My solution: **
public function isTestUser($facebook_uid){
FacebookSession::setDefaultApplication(config_item('APP_ID'), config_item('APP_SECRET'));
$session = FacebookSession::newAppSession();
try {
$session->validate();
} catch (FacebookRequestException $ex) {
// Session not valid, Graph API returned an exception with the reason.
return FALSE;
} catch (\Exception $ex) {
// Graph API returned info, but it may mismatch the current app or have expired.
return FALSE;
}
$request = new FacebookRequest(
$session,
'POST',
'/'.facebook_uid,
array(
'password' => 'password',
'name' => 'Test Username'
)
);
try {
$response = $request->execute();
} catch (FacebookRequestException $ex) {
// (#200) You do not have sufficient permissions to perform this action
// (#100) Invalid password
// (#100) Invalid name
return ($ex->getCode() == 100) ? TRUE : FALSE;
} catch (\Exception $ex) {
return FALSE;
}
/*
object(Facebook\GraphObject)#1714 (1) {
["backingData":protected]=>
array(1) {
["success"]=>
bool(true)
}
}
*/
$graphObject = $response->getGraphObject();
return ($graphObject) ? TRUE : FALSE;
}