I am trying to perform a simple oauth2 request in PHP for the Xero API. The code request works however when I attempt to request a token I receive the following error:
"error":"invalid_client"
My header for the token request looks as follows:
"Authorization: Basic WQGd1xX=="
"content-type: application/x-www-form-urlencoded"
I am following this guide https://developer.xero.com/documentation/oauth2/auth-flow point 3) Exchange the code. Is my header correct or am I missing something? I am using the php function base64_encode as follows:
$auth=base64_encode(OAUTH2_CLIENT_ID + ":" + OAUTH2_CLIENT_SECRET)
This was the function in php
base64_encode(OAUTH2_CLIENT_ID.":".OAUTH2_CLIENT_SECRET);
I also need to pass this in curl_setopt_array
CURLOPT_CAINFO => $caFile
I am working in header authorization in plain PHP to arrange given results.
In any way i try to arrange them with
$headers=array(
'Authorization:'Signature,
'Content-Type: application/json'
);
but nothing seems to be working.
And the result I need to arrange is:
Authorization: Signature keyId="57502612d1bb2c0001000025fd53850cd9a94861507a5f7cca236882",algorithm="hmac-sha1",headers="date x-mod-nonce",signature="WBMr%2FYdhysbmiIEkdTrf2hP7SfA%3D"
I have come around a strange issue that when I am passing authorization header like below,
CURLOPT_HTTPHEADER => array(
"Authorization: Basic XXXXXXXXXX",
"Content-Type: application/json",
),
If I send like this then Authorization is not showing in request header.
But If I have Auth details like below,
CURLOPT_USERPWD=>"xxxxx:xxxxxx",
it will adding required Authorization header as I needed in htpp request.
So what is the issue in this? why it is not simply work as given in first code snippet? Also I am not able to send custom header, custom headers are also not available in request body.
I use the following code to get the bearer token:
$token = base64_encode($client_id.':'.$client_sec);
$data = array ('grant_type' => 'client_credentials');
$data = http_build_query($data);
$header = array(
'Authorization: Basic '.$token,
'Content-type: application/x-www-form-urlencoded;charset=UTF-8',
'Content-Length: ' . strlen($data)
);
$options = array(
CURLOPT_HTTPHEADER => $header,
CURLOPT_HEADER => false,
CURLOPT_URL => 'https://api.twitter.com/oauth2/token',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => $data
);
$ch = curl_init();
curl_setopt_array($ch, $options);
$result = curl_exec($ch);
curl_close($ch);
print_r($result);
exit;
But output all the time:
{"errors":[{"label":"authenticity_token_error","code":99,"message":"Unable to verify your credentials"}]}
What I doing wrong?
After fighting with this problem for a while I found the problem was I was making the call to /oauth2/token using Advanced Rest Client from a browser I was already logged into Twitter with. After logging out of Twitter and making the API call again it worked fine.
Short answer: make sure you do not already have an active session logged into Twitter when attempting to request a Bearer token.
I struggled with this for awhile and none of the answers I've found seemed to help. The documentation for the error is also a vague "something went wrong".
My problem is that I was using a mashup of code I found, and the headers weren't used correctly:
$headers = array(
'Authorization' => 'Basic ' . base64_encode($appid . ':' . $secret), // WRONG!!!
'Authorization: Basic ' . base64_encode($appid . ':' . $secret), // Correct!
'Content-Type: application/x-www-form-urlencoded;charset=UTF-8', // Correct!
);
For me, the problem was that the Authorization header was using key-value format, while the content-type header was not. This broke the authorization header.
Here are some other things to check that also relate to error 99:
Verify that your credentials are correct and base64 encoded (see above)
Make sure the request is using POST
Ensure the content-type is set (see above)
Make sure you included grant_type=client_credentials as a post field.
SSL is required, make sure that is being used (https://)
Try verbose logging to help debugging. It should include SSL certificate information, your authorization header, and content type header. This won't show the grant_type field though, only headers.
If everything looks OK but it still won't work, you might be getting rate limited. Rate limits reset every 15 minutes.
When you finally get your access token, make sure you cache it to avoid rate limiting. You get 450 requests every 15 minutes, I believe. Half of that will be spent on getting your access token if you don't cache it!
There's an accepted answer here already but just in case someone stroll to this post and had the same issue I did...
Twitter docs for reference -> OAuth 2.0 docs
Misconception #1: The Authorization String is generated using the consumer key (aka API-Key) and consumer secret (aka API Secret Key). The display of those credentials in the UI on developer.twitter.com is less apparent than that of apps.twitter.com. Nonetheless common RIF problem.
Misconception #2: This one is not really an misconception but an implementation error when base64 encoding the url concatenated Consumer Key+":"+Consumer Secret. If not doing this programmatically be sure to check for whitespaces anywhere (especially around the :) in the concatenated string you are base64 encoding.
Just a tad bit advice as well postman has a wonderful utility that makes the rest call to retrieve an oauth2.0 token (as well as other auth tokens) this was useful for me when trying to consume api's with the that required an oauth1.0 token
After fighting with this problem, i finally come up with the solution. Twitter is not actually sending the right message if error exist anywhere.
When i send request from curl, it works fine but when through code. i was having same error {"errors":[{"label":"authenticity_token_error","code":99,"message":"Unable to verify your credentials"}]}
So what i got, problem was lying with Access control header. setting these header does not work for me
xhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
xhttp.setRequestHeader('Access-Control-Allow-Headers', '*');
xhttp.setRequestHeader('Access-Control-Allow-Origin', '*')
xhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
as a workaround i just used this url to bypass request to handler cors
https://cors-anywhere.herokuapp.com/https://api.twitter.com/oauth2/token
added "https://cors-anywhere.herokuapp.com/" before the actual url and it began to work. hope someone may face this issue in problem
Twitter OAuth 2.0 Bearer Token:
Step 1: Encode consumer key and secret
A - Concatenate the encoded consumer key, a colon character ":", and the encoded consumer secret into a single string.
B - Base64 encode the string from the previous step.
Example function: convertStringBase64("Api key" +":"+"Api key secret")
C- This steps generate you "Authorization"
Step 2: Obtain a Bearer Token
URL: https://api.twitter.com/oauth2/token
The request must be an HTTP POST request.
The request must include an Authorization header with the value of Basic <base64 encoded value from step 1>.
The request must include a Content-Type header with the value of application/x-www-form-urlencoded;charset=UTF-8.
The body of the request must be grant_type=client_credentials.
Example request (Authorization header has been wrapped):
POST /oauth2/token HTTP/1.1
Host: api.twitter.com
User-Agent: My Twitter App v1.0.23
Authorization: Basic eHZ6MWV2RlM0d0VFUFRHRUZQSEJvZzpMOHFxOVBaeVJn
NmllS0dFS2hab2xHQzB2SldMdzhpRUo4OERSZHlPZw==
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Content-Length: 29
Accept-Encoding: gzip
grant_type=client_credentials
If the request was formatted correctly, the server would respond with a JSON-encoded payload:
Example response:
HTTP/1.1 200 OK
Status: 200 OK
Content-Type: application/json; charset=utf-8
...
Content-Encoding: gzip
Content-Length: 140
{"token_type":"bearer","access_token":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAAAAAAAAAAAA%3DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}
For more informtaion look this in Twitter Developer API: https://developer.twitter.com/en/docs/authentication/oauth-2-0/application-only
I need to make a HTTP POST call to a Hudson CI server from CakePHP. The call is parametrized and contains a few key/value pairs. Some of those are in fact files which need to be uploaded.
Since I'm using CakePHP, I'd rather use the HttpSocket class which comes with the framework rather then try and write my own cURL based implementation.
So far, the call looks like this:
$result = $http->post($apiCall, $params, $auth);
$apiCall being the URI of the Hudson REST API.
$params being an array of parameters which go with the POST call.
$auth containing a user/pass for Basic Auth which configured with this is instance of Hudson.
I'm a bit puzzled though: what would I need to do to also included files in my $params array?
We are running Hudson v1.371 which should - as far as I've gathered - support file uploads coming from a parametrized build call.
Thanks!
I don't know if the HttpSocket class handles multipart Http requests. But you can create it manually. I've done this in my CakePHP GData Plugin that includes functionality for uploading videos to YouTube. The save method in the YouTubeVideo model creates a multipart Http request with the first part containing an XML document with meta data about the video and the second part is the binary contents of the video file being uploaded:
// The boundary string is used to identify the different parts of a
// multipart http request
$boundaryString = 'Next_Part_' . String::uuid();
// Build the multipart body of the http request
$body = "--$boundaryString\r\n";
$body.= "Content-Type: application/atom+xml; charset=UTF-8\r\n";
$body.= "\r\n";
$body.= $doc->saveXML()."\r\n";
$body.= "--$boundaryString\r\n";
$body.= "Content-Type: {$data[$this->alias]['file']['type']}\r\n";
$body.= "Content-Transfer-Encoding: binary\r\n";
$body.= "\r\n";
$body.= file_get_contents($data[$this->alias]['file']['tmp_name'])."\r\n";
$body.= "--$boundaryString--\r\n";
$this->request = array(
'method' => 'POST',
'uri' => array(
'host' => 'uploads.gdata.youtube.com',
'path' => '/feeds/api/users/default/uploads',
),
'header' => array(
'Content-Type' => 'multipart/related; boundary="' . $boundaryString . '"',
'Slug' => $data[$this->alias]['file']['name']
),
'auth' => array(
'method' => 'OAuth',
),
'body' => $body,
);
This might get you there.
I tried using typical syntax $result = $HttpSocket->post($url,$data,$request); and it works for me. Btw, there are some instances that httpsocket sends blank data (which I haven't resolved yet) so I used cURL for instead of this.
Also, make sure you're using multipart form for your form. :)