We're using Commission Junction's REST service, which requires we sent an API key in the Authorization header.
We set the header like this:
$ch = curl_init();
curl_setopt_array($ch, array(
// set url, timeouts, encoding headers etc.
CURLOPT_URL => 'https://....',
// ...
));
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Authorization: ' . CJ_API_KEY,
'User-Agent: ' . OUR_USER_AGENT
));
$response = curl_exec($ch);
$info = curl_getinfo($ch);
The problem is that the Authorization header isn't sent (we debugged this by using a local url and doing a var_export($_SERVER) which shows a User-Agent header is set, but not the Authorization header.)
If we change the header name to X-Authorization, it gets sent - but this hasn't helped us as the service specifically requires the Authorization header.
How do we get PHP + cURL to send an arbitrary Authorization header?
The Authorization header isn't included in PHP's $_SERVER variable. To properly debug a request you should use apache_request_headers() which shows we were sending the Authorization header exactly as we wanted.
The problem then moved on to figuring out exactly what to put in the Authorization header given some pretty bad documentation.
When the header is set by the client, then the Authorization-header from the request is included in $_SERVER — not sure if this is something new, but it is now. HTTP-headers get prefixed in the $_SERVER array with HTTP_ which may be something you previously overlooked.
Also, apache_request_headers() is a function which is only defined when you use Apache as a web server. So everyone with nginx etc. is left out.
Demo
On the server-side:
<?php
// server.php
var_dump($_SERVER['HTTP_AUTHORIZATION']);
Test
Start a webserver (requires PHP 5.4):
$ php -S 0.0.0.0:31337 -t .
Make sure server.php is in the current directory.
Use cURL to test:
$ curl -H 'Authorization: FOO' http://0.0.0.0:31337/server.php
string(3) "FOO"
Works. :)
Related
I am using PHP to get JSON from a remote server via file_get_contents command. Here is the piece of code I used:
$opts = array(
'https'=>array(
'method'=>'GET',
'header'=>'Accept-language: en\r\n' .
'Authorization: MAC ["3","ios2.5.0","123","123abc","123=","abc="]\r\n' .
'User-Agent: abc/1.1.1 iOS/10.0.2 iPhone/iPhone7,1\r\n'
)
);
$context = stream_context_create($opts);
$file = file_get_contents('https://www.google.com/v11/file?search=ios&with=users%2Cfiles%2Cquestions', false, $context);
echo $file;
I did a quick debugging:
Using Postman I was able to get the json file with the same header.
I tried a different json from a different url, it works.
I tried a local file, it works.
You have to understand what file_get_contents is. This command is a request to get the file on the server, in this case it is requesting to get https://www.google.com/v11/file/index.html on the server as in one single step. Since your url seems to use header to verify your origin, it might be an ajax request, meaning the server components didn't set up to allow an output from file_get_contents requests, instead they probably accept cURL requests.
So you can use:
curl_exec()
I want to set a header for getting the token from flipkart. I dont know how to set a header in curl. My header should like
curl -u <appid>:<app-secret> https://sandbox-api.flipkart.net/oauth-service/oauth/token\?grant_type\=client_credentials\&scope=Seller_Api
It looks like you're trying to do HTTP basic authentication, or at least that's what the -u option of curl does when used alone like this.
In PHP you would set up basic authentication for a curl request like this, assuming $ch is your curl instance (which you can get with curl_init):
curl_setopt($ch, CURLOPT_USERPWD, 'appid:appsecret');
See the curl documentation on curl_setopt for more information.
In curl command you have to do like this,
curl -u your-app-id:your-app-token https://sandbox-api.flipkart.net/oauth-service/oauth/token\?grant_type\=client_credentials\&scope=Seller_Api
you will get the result like this on success,
{"access_token":"394b7d-418a-43f6-8fd5-e67aea2c4b","token_type":"bearer","expires_in":4657653,"scope":"Seller_Api"}
by using this token you can make further api call's like for example listing api,
curl -H "Authorization:Bearer your-access-token" -H "Content-Type: application/json" -d 'json-here-see-format-in-api-example' https://url-end-point-refer-api-docs
note: you have to create app id and secret in "https://sandbox-api.flipkart.net/oauth-register/login" for sandbox. Don't store the access token it will get expired in certain period of time.
link for api doc's - "https://seller.flipkart.com/api-docs/fmsapi_index.html"
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 am working on proxy with PHP. In my php code I am sending proper required headers and expected to get response body and headers. However I am getting response body correctly as I want but not getting headers properly (supposed to get Status 200 but getting 401). When i traced with firefox I found that SAP URL itsself making 2 request internally by using data which I send. so with my first request it is not authenticated so SAP url itslef managining to send same request again and 2nd time it gives both proper response body with headers. Howevber I php code when I get it i get response body from 2nd response and headers from 1st response.
here is code.
$opts = array(
'http'=>array(
'method'=>"POST",
'content' => $xml_request,
'header'=>array("Host:" . $sap_url,
"Content-Type: text/xml; charset=UTF-8",
$authstring,$xml_request)
)
);
$context = stream_context_create($opts);
$result = file_get_contents($sap_url, false, $context);
$http_res_array = get_headers($sap_url);
You should probably use curl functions instead and do BOTH requests yourself. file_get_contents, does the second request for you, but takes away the possibility to fetch the second headers.
Maybe a little old but anyways:
You're using the get_headers()-function to get the headers. It's documentation states that:
Fetches all the headers sent by the server in response to a [new] HTTP request
It doesn't empathize that this function will actually send a new request to the server and return the response-header for that request. Therefor, the headers can be slightly different.
Since you're using file_get_contents() to load the content, you can use the global $http_response_header-variable right after your request, which will contain the response-header from the last executed request.
I am trying to make a CURL request with some basic authentication (through sending an encrypted header). However, when I try to set the "Host: _" header, the remote server responds with:
Bad Request: Your browser sent a request that this server could not understand.
Here's the code that makes the CURL call. Note that it works as soon as I comment out the "Host: url" header in the $http_header variable. However, it is used on the target server as part of the authentication procedure, so I can not simply remove it.
$curl = curl_init();
$opt = array(
CURLOPT_URL=>$url,
CURLOPT_RETURNTRANSFER=>1,
CURLOPT_HTTPHEADER=>$http_header,
CURLOPT_POST=>1,
CURLOPT_POSTFIELDS=>$data_params,
CURLOPT_CONNECTTIMEOUT=>5, // Timeout to 5 seconds
);
curl_setopt_array($curl, $opt);
// $output contains the output string
$output = curl_exec($curl);
// it closes the session
curl_close($curl);
The contents of $http_header (an associative array):
array
0 => string 'Host: http://localhost/work/myproject'
1 => string 'Content-Type: multipart/form-data'
2 => string 'X-Authorization: Basicauth Z2xZjA2YzJhMzIxYTI1ZmIzZTgxYQ=='
The Host header doesn't accept a full URL, but only a hostname.
In this case the solution is to replace this header:
"Host: $url"
with:
"Host: ". parse_url($url, PHP_URL_HOST) ."
The Host header takes a hostname, not a URL. i.e. localhost, not http://localhost/work/myproject.
cURL should be generating it automatically from CURLOPT_URL though.
The Host header should not contain the protocol, only the host's name. So in this case all you need is localhost:
'Host: localhost'
I don't think either answer answers the asker's question. I've ran into the same problem. From looking at what cURL is sending, it is sending it's own Host header. This means that when you add one you now have two. All servers MUST respond with a 400 error when they see more than one host header.
The trick is get cURL not to generate it's own header (which I haven't figured out how to do yet) or don't send yours. In your case it seems not sending yours is fine. But, for what I'm doing, I have to send mine.