With help from #CarlZhao I am finally getting a good understanding of the difference between OAuth and Graph. I am building the capability in my app for users to post messages to a team channel. So far I can list teams, channels, and delete channels. I am having a hard time trying to send a chatMessage. I understand that because sending a chatMessage is a delegated permission and not an application permission so from my understanding I have to use the accessToken created from OAuth when the user authenticated with my app.
What I am doing is saving that token in my database so I can call it when I am trying to send a chatMessage. Not sure if that is correct. So in my code, I am creating a new Graph instance, but I am using the access token of the user and not the token of the graph.
$useraccesstoken = "************************************";
// create a new OAuth graph from useraccesstoken
$graph_message = new Graph();
$graph_message->setAccessToken($useraccesstoken);
// post message
$data = [
'body' => [
'content' => 'This is a message from the API I made it works'
],
];
$message = $graph_message->createRequest("POST", "/teams/$group_id/channels/$channel_id/messages")
->addHeaders(array("Content-Type" => "application/json"))
->attachBody($data)
->setReturnType(Model\User::class)
->execute();
This is producing no errors, but nothing happens and the chatMessage is not posted. I have double-checked and my $group_id and $channel_id are correct.
Am I using the $useraccesstoken correctly? can I start a new Graph() instance with the $useraccesstoken?
Yes, you could start a new Graph() instance with the $useraccesstoken.
The graph API of sending messages doesn't return User::class. Try your code with
->setReturnType(Model\ChatMessage::class)
The access token is invalid for one hour by default, see here. You could not use it all the time, so it seems you don't need to store in the database. It's better to refresh token before the access token expires, and this step shows you how to refresh token.
The default is 1 hour - after 1 hour, the client must use the refresh
token to (usually silently) acquire a new refresh token and access
token.
Related
I am building a portal where multiple users can log in to their multiple Gmail accounts. I have successfully retrieved the token value, However, I want to store that in my database but I am unable to store it.
Below is the code I am using:
function mInititalize(){
$client = new Google_Client();
$client->addScope('https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email https://mail.google.com/');
$client->setClientId(Config('gmail.client_id'));
$client->setClientSecret(Config('gmail.client_secret'));
$client->setRedirectUri('http://localhost:81'.Config('gmail.redirect_url'));
$loginURL = $client->createAuthUrl();
return redirect($loginURL);
}
After Redirection or user login
function mGetToken(){
$token = $client->fetchAccessTokenWithAuthCode( 'code'); // here i get the 'code' from login URL
I pass this code to get token I successfully get token
$oAuth = new Google_Service_Oauth2( $client);
$userData = $oAuth->userinfo_v2_me->get(); // get current user detail
}
I want to store $token value in database, but I am getting error message
>Serialization of 'Closure' is not allowed
Please anyone help me to solve this issue. Thanks.
I would suggest storing OAuth credential information for the Google API, not in your database, but through the API itself. If you're intending to use it any authentication manner, you'll run into problems, as the docs state:
Access tokens periodically expire and become invalid credentials for a related API request. Google Identity Platform: Using OAuth 2.0 for Web Server Applications
But, the same docs also show a way that you can set or retrieve the token natively within the API. Since it's data relating to google's auth'ing process, and since it might go stale if you store it, it seems best to just let them handle it and work with the API. The same source:
If you need to apply an access token to a new Google_Client object—for example, if you stored the access token in a user session—use the setAccessToken method:
$client->setAccessToken($access_token);
$client->getAccessToken();
I requested authorization for a public application to be able to access store data via the Shopify API.
The store successfully authorized my application via an authorization request URL such as
https://some-store.myshopify.com/admin/oauth/authorize?client_id=123abc&scope=read_inventory%2Cread_products&redirect_uri=http%3A%2F%mysite.com%2Fauth.php&state=123456
and the response was passed back to my application. This response (containing the code that can be exchanged for a permanent access token) was mishandled by my application (an error on the page meant that the access token was not stored).
Everything I read regarding requesting these tokens involves authorization by the store - but given the store has already authorized my application, passed back the code and that code has already successfully been exchanged for a token: is there a way my application can request that same token or a fresh one using my API keys given that the application is already authorized?
The only method I currently can find for requesting a token requires starting back at the beginning and fetching a code for exchange etc.
I working in PHP and using Luke Towers' php shopify wrapper
This stage was completed successfully:
function check_authorization_attempt()
{
$data = $_GET;
$api = new Shopify($data['shop'], [
'api_key' => '123',
'secret' => '456',
]);
$storedAttempt = null;
$attempts = json_decode(file_get_contents('authattempts.json'));
foreach ($attempts as $attempt) {
if ($attempt->shop === $data['shop']) {
$storedAttempt = $attempt;
break;
}
}
return $api->authorizeApplication($storedAttempt->nonce, $data);
}
$response = check_authorization_attempt();
and I would have been able to read the access token from :
$access_token = $response->access_token;
But this was the stage at which my application hit an error in accessing a database in which to write said token.
I cannot repeat it without repeating the auth request because the data in $_GET that's passed to this function comes from Shopify's response to the shop owner authorizing the access, and includes amoung other things the code for exchange.
You have to re-ask for authorization. It is no one's fault but yours that your persistence layer code was incorrect. So there is nothing you can do to change that. Ensure your code works. Since the client has no token in your App persistence layer, your App will retry the authorization token exchange. They do not have to delete your App first. So basically, the next time your client tries to use the App, YES they will asked to approve it, but who cares, they will, and you'll get a good auth token to store. You have fixed your code (right), so that will work. You are one step closer to glory.
Shopify does return the Permanent Access Token, but the ACCESS_MODE must be "Offline" for the token to be permanent.
With ACCESS_MODE offline, your app receives the permanent access token
to make requests whenever you want, without the user's permission.
Documentation:
https://shopify.dev/tutorials/authenticate-with-oauth#step-2-ask-for-permission
https://shopify.dev/concepts/about-apis/authentication#api-access-modes
I'm new to bigcommerce and jwt tokens. I'm trying to get the customer login api to work on a trail store. But have not been able to successfully login a customer automatically.
I got it to work once or twice, but now it doesn't work anymore, and an unable to figure out the odd behavior since nothing changed with the code. I've tried googling if anyone else has had issues with the customer login api but have found nothing.
I've gone thru the tutorial on https://developer.bigcommerce.com/api/v2/#customer-login-api and copied the example provided.
Is there something I'm missing with the code below?
I've gone to developer.bigcommerce.com and created a draft app.
I got the Client ID and Client Secret from the draft app.
I've gone into my trial store and successfully installed the draft app.
I've tested this page on SSL as well.
Could it be because it's a trail store and it will only work if it's a real store?
Thanks.
Here is the php code below:
include "vendor/autoload.php";
use Bigcommerce\Api\Client as Bigcommerce;
use Firebase\JWT\JWT;
function getCustomerLoginToken($id, $redirectUrl = '', $requestIp = '') {
/*
if (empty(self::$client_secret)) {
throw new Exception('Cannot sign customer login tokens without a client secret');
}
*/
$payload = array(
'iss' => '#MyApp1's Client ID#',
'iat' => time(),
'jti' => bin2hex(random_bytes(32)),
'operation' => 'customer_login',
'store_hash' => '#Store Hash#',
'customer_id' => $id
);
if (!empty($redirectUrl)) {
$payload['redirect_to'] = $redirectUrl;
}
if (!empty($requestIp)) {
$payload['request_ip'] = $requestIp;
}
return JWT::encode($payload, "#MyApp1's Client Secret#", "HS256");
}
$jwt = getCustomerLoginToken(1);
header("Location: " . 'https://store-#Store Hash#.mybigcommerce.com/login/token/' . $jwt);
exit();
There are a couple of constraints that can cause errors:
The app must be installed on the store (seems like you're all good here - you can also test with an API token created locally in the store - https://support.bigcommerce.com/articles/Public/Store-API-Accounts/)
The app must have the Login OAuth scope
The JWT URL must be visited within about 30 seconds, or it won't work.
The computer/server that's generating the JWT needs to have a clock that's synchronized, otherwise your IAT value will appear to have been created more than 30 seconds ago or even in the future which will fail.
The URL can only be visited exactly once - if anything runs a GET request against it other than the intended end user's browser, it won't work. For example, if you send the URL in a Slack message, slack will try to preview the link by visiting it and therefore invalidate it.
It's good to double-check that your JWT is valid at https://jwt.io/
BigCommerce support has access to logs which can shed more light on the situation if you've ruled out the above.
Hope this helps!
do not need app client_id and Secret code , you need to api's client_id and secret code
I am using php oauth2 library from this github repo.
PHP oauth2 library
Whenever i send a refresh token, I receive new access token with old scopes.
But i want to change the scopes returned with new access token.
When i first generate a token using user credentials grant type, I get the supported scopes for the user and store them this way.
$defaultScope = implode(" ", $scopes);$memory = new OAuth2\Storage\Memory(array('default_scope' =>$defaultScope));
$scopeUtil = new OAuth2\Scope($memory);
$this->server->setScopeUtil($scopeUtil);
$this->server->handleTokenRequest(OAuth2\Request::createFromGlobals())->send();
where $scopes is an array
for example $scopes=array("ADDUSER","EDITUSER","EDITROLE");
similarly , if i send refresh token using refresh_token grant type and run this with modified $scopes
for example $scopes=array("ADDUSER", "EDITROLE");
$defaultScope = implode(" ", $scopes);$memory = new OAuth2\Storage\Memory(array('default_scope' =>$defaultScope));
$scopeUtil = new OAuth2\Scope($memory);
$this->server->setScopeUtil($scopeUtil);
$this->server->handleTokenRequest(OAuth2\Request::createFromGlobals())->send();
I receive same old scopes("ADDUSER EDITUSER EDITROLE") which were set when new access token generated using user credentials grant type.
SO is there a way to change scopes when new access token is generated using refresh token ?
or am i doing something wrong here?
A Client can "down-scope" when it asks for a new access token in the refresh token grant, see the documentation around scope in the spec here: https://www.rfc-editor.org/rfc/rfc6749#section-6 Yet your Authorization server may or may not support that.
I am building a restful API (PHP) to serve iOS and Android applications and I would like to implement facebook login on both apps.
The flaw is like the following :
Clients ( ios or Android ) login with facebook and send an access_token to the restful api
verify if the access_token is authorized to use the application
If token is valid, get user data from graph.
Merge accounts and generate token for different queries.
For security purpose to avoid getting random tokens thatthey don't belong to my APP, I would like to make a test call to check if a token is authorized and valid or not ?
I know many similar questions might be already answered but none of them really give me the right answer and I don't really have experience with facebook graph.
I found this solution :
https://graph.facebook.com/oauth/access_token?client_id=APP_ID&client_secret=SECRET_APP_ID&grant_type=fb_exchange_token&fb_exchange_token=ACCESS_TOKEN
This works somehow .. it whether give me an error, or an access token (string format not JSON) and I am not sure if this is the best way to test or not.
Note: I am still in early stage of development, if you have any suggestion on my flow please let me know, I might be doing things the wrong way ?
What works for my application (code with explanation below)...
$fb = new Facebook\Facebook([
'app_id' => 'XXXXXXX',
'app_secret' => 'XXXXXXX',
'default_graph_version' => 'v2.5',
]);
// My app pulls users' access tokens & page ID from a database here and stores in $fb_access_token & $fb_page_id variables
$fb_access_token = 'YOU OR YOUR USERS ACCESS TOKEN GOES HERE';
$fb_page_id = 'ID OF FACEBOOK PAGE OR USER';
$fb->setDefaultAccessToken($fb_access_token);
// CHECK IF ACCESS TOKEN SITLL WORKS
try{
$page = $fb->get('/'.$fb_page_id.'?fields=id', $fb_access_token);
} catch(Facebook\Exceptions\FacebookResponseException $e) {
echo 'Graph returned an error: ' . $e->getMessage();
$graphError = 'Yes';
} catch(Facebook\Exceptions\FacebookSDKException $e) {
echo 'Facebook SDK returned an error: ' . $e->getMessage();
$sdkError = 'Yes';
}
// IF ACCESS TOKEN STILL WORKS...CONTINUE WITH SCRIPT. THIS PREVENTS ACCESS TOKEN FAILURE FROM BREAKING SCRIPT.
if(!isset($graphError) && !isset($sdkError)){
// CONTINUE WITH SCRIPT
}
Explanation: you are taking the access token in question, and attempting to make a GET Request to the Facebook API. Only continue with the rest of your script IF there are NO ERRORS.
You could also add an ELSE statement at the end to maybe redirect the user to a page where they can re-authenticate their account/access token, depending on what your app is doing.
My app uses a couple of WHILE Loops to go through my database of users, and depending on certain column/cell values, it POSTS to their Facebook page for them...
Before this solution...when the Loop came across an invalid Access Token, it "broke" and did not execute the script for the rows following the "unauthenticated user" because it was making a failed request.
Hope this helps someone!
“Random” tokens would not work anyway. (Tokens issued by Facebook are encrypted, so the API can tell whether a token is genuine, or just "random". At most you'd need to worry about what a user possible could using a token for a different app, or one they themselves granted more permissions than you asked them for.)
Just request the user details using the access token you got - if it is not valid because someone tried to “fake” it, then the API response will tell you so.
The docs have a chapter about securing API requests, go check that out as well: https://developers.facebook.com/docs/graph-api/securing-requests