Spotify API - error retrieving access token during authorization process - php

I'm building my first Spotify application and right now I'm tackling the authorization process.
So far I have been successful in retrieving my State and Code from https://accounts.spotify.com/authorize
and now I'm sending a POST request via PHP CURL request to acquire my access token.
Spotify's instructions for this step
I keep getting the following JSON error response indicating that my grant_type is not valid and it offers me three valid options:
{"error":"unsupported_grant_type","error_description":"grant_type must be client_credentials, authorization_code or refresh_token"}bool(true)
If you look at my code below, I believe I have set the correct grant_type of "authorization_code" but I'm getting the error. I have highlighted with '******' the code snippet of what I believe to be the correct line of code.
Can anyone see what I'm doing incorrectly? Here's the code I'm using to send the request:
// Get access tokens
$ch = curl_init();
// Specify the HTTP headers to send.
//Authorization: Basic <base64 encoded client_id:client_secret>
$ClientIDSpotify = "[my spotify app id]";
$ClientSecretSpotify = "[my secret code]";
$authorization = base64_encode ( "{$ClientIDSpotify}:{$ClientSecretSpotify}" );
$http_headers = array(
"Authorization: Basic {$authorization}"
);
curl_setopt( $ch, CURLOPT_HTTPHEADER, $http_headers );
curl_setopt( $ch, CURLOPT_POST, true);
$spotify_url = "https://accounts.spotify.com/api/token";
curl_setopt( $ch, CURLOPT_URL, $spotify_url );
// *************************************************
// HERE'S WHERE I CORRECTLY SPECIFY THE GRANT TYPE
// *************************************************
$data['grant_type'] = "authorization_code";
$data['code'] = $authorizationCode;
$callbackURL = "[my callback URL]";
$data['redirect_uri'] = $callbackURL;
curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
$response_json = curl_exec( $ch );
curl_close( $ch );
}

Just as a note to the last comment about switching to http_build_query, I had to URLDECODE the data, in order for Spotify to recognize it. Try using this line instead.
curl_setopt($ch, CURLOPT_POSTFIELDS, urldecode(http_build_query($data)));

Seems to me like the POST body isn't being formatted correctly, everything else looks good.
My limited understanding of PHP tells me that your POST body looks like
{
"fields" : {
"code" : $authorizationCode,
"grant_type" : "authorization_code",
"redirect_uri" : "http://www.example.com/spotify/callback/index.php"
}
}
Of course, what you'd like to send is just
{
"code" : $authorizationCode,
"grant_type" : "authorization_code",
"redirect_uri" : "http://www.example.com/spotify/callback/index.php"
}
Therefore, try to set the $data object with
$data['grant_type'] = "authorization_code";
$data['code'] = $authorizationCode;
$data['redirect_uri'] = $callbackURL;
or even shorter
$data = array("grant_type" => "authorization_code", "code" => $authorizationCode, "redirect_uri" => $callbackURL);
Hope this helped!

OK, so I dug a little digging and found some code in the PHP CURL manual comments section. The problem with Spotify's documentation is it doesn't specify the format of the POST data to be sent. I assumed since Spotify was sending me JSON data that I should be sending my data in JSON format as well. So I was formatting the POST data as such:
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
After reading through some documentation I decided to try this instead:
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
I got exactly the results I needed:
{"access_token":"[long access token]","token_type":"Bearer","expires_in":3600,"refresh_token":"[long refresh token]"}
Thank you, Michael, for attempting to assist!

Related

Why is the curl code not executed in PHP script? (WAMP Server)

I am trying to get a token to use the Microsoft Graph API (https://learn.microsoft.com/en-us/graph/auth-v2-user?context=graph%2Fapi%2F1.0&view=graph-rest-1.0) via Curl. I have set up a simple Php file with this function:
function getToken() {
echo "start gettoken";
var_dump(extension_loaded('curl'));
$jsonStr = http_build_query(Array(
"client_id" => "***",
"scope" => "https://graph.microsoft.com/.default",
"client_secret" => "***",
"grant_type" => "client_credentials"
));
$headers = Array("Content-Type: application/x-www-form-urlencoded", "Content-Length: " . strlen($jsonStr));
$ch = curl_init("https://login.microsoftonline.com/***.onmicrosoft.com/oauth2/v2.0/token");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonStr);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$token = curl_exec($ch);
echo "test after curl";
return $token;
curl_error($ch);
}
However, what I want to know is why the curl request is not working. Also the echo after the curl codeblock is not being executed, while 'start gettoken' is. PHP_curl is enabled in my WAMP. Why is this?
Are you sure CURL is enabled because that code you have posted is ok and giving echo response before and after curl execution.
you're sending the token request in a JSON-format, and then you're lying to the server saying it's application/x-www-form-urlencoded-encoded when it's actually application/json-encoded! since these 2 formats are completely incompatible, the server fails to parse it, and... ideally it should have responded HTTP 400 bad request (because your request can't be parsed as x-www-form-urlencoded)
anyhow, to actually send it in the application/x-www-form-urlencoded-format, replace json_encode() with http_build_query()
also get rid of the "Content-Length:"-header, it's easy to mess up (aka error-prone) if you're doing it manually (and indeed, you messed it up! there's supposed to be a space between the : and the number, you didn't add the space, but the usual error is supplying the wrong length), but if you don't do it manually, then curl will create the header for you automatically, which is not error-prone.

How to post JSON data and a request token(in the header) in cURL php

I want to send the login credentials and a pin number as a JSON data and the request token as my http header.
initially it's like this,
{
"Username":"admin",
"Password":"root123",
"PinCodeā€ : "hello321"
}
and I need to post my Request Header token as well.
and if the request is ok, I should get JSON response as follow,
{
"Title": "Mr.",
"Name": "Pushkov",
"Age": "18"
}
I'm trying to do it in cURL PHP. Below is my controller code.
I tried to save my request token to a variable and pass it in header and post username, password and pinnumber as JSON data. if the login success than user info should be displayed. but I'm struggling to move ahead from here. How can I achieve that?
public function send_data() {
$url='http://samplesite.azurewebsites.net/api/member/loginmember';
$ch = curl_init('http://localhost/main');
$data = array("Username" =>$this->input->post('un'), "Password" =>$this->input->post('pw'), "PinCode" =>$this->input->post('PinCode'));
$data_string = json_encode($data);
//echo $data_string;
// Disable SSL verification
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
// Will return the response, if false it print the response
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string))
);
// Set the url
curl_setopt($ch, CURLOPT_URL,$url);
// Execute
$result=curl_exec($ch);
//var_dump(json_decode($result, true));
$data2=json_decode($result, true);
$ref_id = ( ( is_array( $data2["UserCode"] ) ? implode(", ", $data2["PinCode"]) : $data2["RequestToken"] ) ); // array to string
$acc_t = $data2["RequestToken"];
$uun = $data2["UserCode"];
//echo $acc_t;
// Closing
curl_close($ch);
//print $result ;
}
In your code above, you set a URL to curl_init() and you also passed another one in CURLOPT_URL option. You can't do that in one cURL request.
If you are using OAuth 2.0 you can set the access token to CURLOPT_XOAUTH2_BEARER option.
More information, please refer to PHP manual on cURL Functions.

Modifying php script to have POST request in cURL for a JSON upload

I have an existing PHP script, which essentially connects to 2 databases each on a different server and performs a few MySQL queries on each. The ultimate results are stored in a data array which is used to write said results into a JSON file.
All of this works perfectly. The data is inserted into the mysql table correctly and the JSON file is exactly the way it should be.
However, I need to add a block to the end of my script that makes a POST request to one of our affiliate's API and upload the info there. We're currently manually uploading this JSON file to the api instance but we have the configuration data for their server to use in a POST request now so that when this script is run it automatically sends the data rather than us having to manually update it.
The main thing is I'm not exactly sure how to go about that. I've started with code for doing this but I'm not familiar with cURL so I don't know the best way to structure this in php.
Here is an example the affiliate gave me in cURL command line syntax:
curl \
-H "Authorization: Token AUTH_TOKEN" \
-H "Content-Type: CONTENT_TYPE" \
-X POST \
-d '[{"email": "jason#yourcompany.com", "date": "8/16/2016", "calls": "3"}]'
\
https://endpoint/api/v1/data/DATA_TYPE/
I have my auth token, my endpoint URL and my content type is JSON, which can be seen in my code below. Also, I have an array instead of the example for the body above.
and here's the affected part of my code:
//new array specifically for the final JSON file
$content2 = [];
//creating array for new fetch since it now has the updated extension IDs
while ($d2 = mysqli_fetch_array($data2, MYSQLI_ASSOC)) {
// Store the current row
$content2[] = $d2;
}
// Store it all into our final JSON file
file_put_contents('ambitionLog.json', json_encode($content2, JSON_PRETTY_PRINT ));
//Beginning code to upload to Ambition API via POST
$url = 'endpoint here';
//Initiate CURL
$ch = curl_init($url);
//JSON data
$jsonDataEncodeUpload = json_encode($content2, JSON_PRETTY_PRINT);
//POST via CURL
curl_setopt($ch, CURLOPT_POST, 1);
//attach JSON to post fields
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonDataEncodeUpload);
//set content type
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
//execuate request
$postResult = curl_exec($ch);
So, like I said, nothing about the file or the data needs to be changed, I just need to have this cURL section take the existing array that's being written to a JSON file and upload it to the API via post. I just need help making my php syntax for curl match the command line example.
Thanks for any possible help.
Have you tried with file_get_contents ( http://en.php.net/file_get_contents ).
$postdata = http_build_query(
array(
'var1' => 'some content',
'var2' => 'doh'
)
);
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
$context = stream_context_create($opts);
$result = file_get_contents('http://example.com/submit.php', false, $context);
I have found the answer on stackoverflow How to post data in PHP using file_get_contents?
Here is worked example of code. Check $err may be it will be helpful.
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $_POST('data'));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type:application/json']);
$result = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch);
curl_close($ch);

Oauth2.0 server in php very simple implementation - use access_token with POST

Im building a small API and want to use oAuth2.0 for login and token handling.
I have followed this step by step guide to get started: https://bshaffer.github.io/oauth2-server-php-docs/cookbook/
Now this all works fine I can get my access_token by sending post request to the token.php from the link guide above:
//GET TOKEN
$params = array(
"client_id" => "testclient",
"client_secret" => "testpass",
"grant_type" => "client_credentials");
$test=curl_req($pageURL."/api/v1/"."token.php", $params, "POST");
echo "<pre>";
print_r($test);
This outputs, as espected:
stdClass Object
(
[access_token] => b53a01a66d20760a8afef05bf36951eeba6b886d
[expires_in] => 3600
[token_type] => Bearer
[scope] =>
)
Now I have the token and want to do a request to resource.php as in the example link above. But I want to do it with POST, not GET as in the step by step guide link above.
Is it possible to get the access_token verified with POST and if yes, how?
the command in the example is:
curl http://localhost/resource.php -d 'access_token=YOUR_TOKEN'
which actually issues a POST since CURL will revert to HTTP POST if the "-d" flag is provided, see the docs on "-d" at http://curl.haxx.se/docs/manpage.html
In general the OAuth server will be able to pickup to token if sent in any of the standardized ways, so a) in an "access_token" POST parameter, b) in an "access_token" query parameter (GET), or c) in the "Authorization: bearer" header. The last option is actually the preferred method since it prevents the access_token from mixing with user data (i.e. works also in non-REST environments) or ending up in logs.
I ended up sending the access_token and POST data this way
$params = array(
"client_id" => "testclient",
"client_secret" => "testpass",
"grant_type" => "client_credentials");
//GET TOKEN
$test=curl_req($pageURL."/api/v1/"."token.php", $params, "POST");
// ^-- Im not using the curl_req2 function below here
// Im using a function, from my old question here: https://stackoverflow.com/questions/13420952/php-curl-delete-request
echo "<pre>";
print_r($test);
$token = array(
"access_token" =>$test->access_token);
$data = array(
"Some" =>"data",
"More" =>"datatatat"
);
// GET ACCESS using POST and POST data
$test2=curl_req2($pageURL."/api/v1/User.php",$token, $data , "POST");
print_r($test2);
function curl_req2($url,$token,$data,$req)
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $req);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' . $token['access_token']));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
//$result = json_decode($result);
curl_close($ch);
return $result;
}

Convert URL encoded to JSON object

I've never worked with JSON objects before but I think this is probably the best route for what I'm trying to achieve (if you think there is a better option please let me know, though I cannot use SOAP)
I am trying to submit some data via POST to an API in order to add some data to a newsletter list which is stored as XML. Here is the documentation for the request I am trying to complete: https://api.dotmailer.com/v2/help/wadl#PostAddressBookContacts
When visiting the API url and authenticating I see data laid out in the following:
<ArrayOfApiContact xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://apiconnector.com">
<ApiContact>
<Id>1058905201</Id>
<Email>email#email.co.uk</Email>
<OptInType>Unknown</OptInType>
<EmailType>Html</EmailType>
<DataFields i:nil="true"/>
<Status>Subscribed</Status>
</ApiContact>
</ArrayOfApiContact>
So I have attempted to replicate as such, using a variable instead of a hardcoded email:
$xml = array(
'id' => urlencode('123456789'),
'email' => urlencode($useremail),
'optInType' => urlencode('Unknown'),
'emailType' => urlencode('Html'),
'dataFields' => urlencode(':null'),
'status' => urlencode('Subscribed')
);
I am combining the array here:
foreach($xml as $key=>$value) { $xml_string .= $key.'='.$value.'&'; }
And then using cURL to POST the request to the API, with my authentication (apipassword and apiemail which the username)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$auth_url);
curl_setopt($ch,CURLOPT_POST, count($xml));
curl_setopt($ch,CURLOPT_POSTFIELDS, $xml_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($ch, CURLOPT_USERPWD, "$apiemail:$apipassword");
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); //get status code
$result = curl_exec ($ch);
curl_close ($ch);
I have already tested this without the POST and I do indeed get the data back in this form:
[{"id":1058905201,"email":"email#email.co.uk","optInType":"Unknown","emailType":"Html","dataFields":null,"status":"Subscribed"}]
So I know the auth is working and the data is coming back correctly, but now when I try to POST my data I get:
{"message":"Content type \"application/x-www-form-urlencoded\" is not supported. Please use one of: application/json,application/xml ERROR_CONTENT_TYPE_IS_NOT_SUPPORTED"}
So how do I convert my array which is URL encoded into something readable by the API?
Try a content type of application/json like so:
curl_setopt($ch, CURLOPT_HTTPHEADER, array('content-type: application/json'));
Two things here (a) content-type header JSON missing and (b) payload not JSON.
// setup request to send json via POST.
$payload = json_encode(array("json"=> $data)); // or json_encode(array($data));
curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload );
// set proper content-type for sending JSON
curl_setopt( $ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));

Categories