[SalesForce] Salesforce REST API with PHP, INVALID_SESSION_ID after successful authentication

I connected my web app to salesforce and successfully followed all the steps in the web server OAuth flow to get an access_token and related info:
https://developer.salesforce.com/docs/atlas.en-us.api_placeorder.meta/api_placeorder/intro_understanding_web_server_oauth_flow.htm

Every step seems to return the expected set of results specified in the docs, but when I try to make requests with the final access token, I get an error saying my session ID is invalid. Here is my code:

    // Execute a request for access_token and related info
    $output = json_decode(curl_exec($ch));
    $info = curl_getinfo($ch);
    curl_close($ch);

    // Display output and curl info
    var_dump($info);
    var_dump($output);

    // Extract token and instance_url from output
    $sf_url = $output->instance_url . '/services/data/v35.0/';
    $sf_auth = 'Bearer ' . $output->access_token;

    // Execute a new cURL request with auth values
    $ci = curl_init();
    curl_setopt($ci, CURLOPT_URL, $sf_url);
    curl_setopt($ci, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ci, CURLINFO_HEADER_OUT, true);
    curl_setopt($ci, CURLOPT_HTTPGET, true);
    curl_setopt($ci, CURLOPT_HTTPHEADER, array(
        'Accept' => 'application/json',
        'Authorization' => $sf_auth,
        'X-PrettyPrint' => 1
    ));

    $output2 = curl_exec($ci);
    $info2 = curl_getinfo($ci);
    curl_close($ci);

    // Display output and curl info
    var_dump($info2);
    var_dump($output2);

The dump from $output gives:

object(stdClass)#194 (7) { 
["access_token"]=> string(112) "xxxxxxxxxxxxxxxx" 
["signature"]=> string(44) "xxxxxxxxxxxxxx" 
["scope"]=> string(3) "api" 
["instance_url"]=> string(27) "https://cs10.salesforce.com" 
["id"]=> string(68) "https://test.salesforce.com/id/xxxxx/xxxxx"
["token_type"]=> string(6) "Bearer" 
["issued_at"]=> string(13) "1453358xxxxxx" }

The dump from $output2 gives:

string(75) "[{"message":"Session expired or invalid","errorCode":"INVALID_SESSION_ID"}]"

Any help here would be much appreciated.

* UPDATE *

I also tried using the username-password auth flow, and I'm getting the same result. I can get an access token, but I can't query the database, and the error being returned is the same INVALID_SESSION_ID.

I used the Postman plugin for chrome, passed in all the same info for the username-password flow, and it works for making queries. I'm not sure what is being done differently, seems like all the data being passed is the same.

* Update 2 *

The dumps from curl_getinfo() show that the headers aren't being set in the second curl request, but I can't see why.

The var_dump from $info gives:

array(27) { 
["url"]=> string(294) "https://test.salesforce.com/services/oauth2/token?client_id=xxxxxxx&client_secret=xxxxxxxx&username=xxxxxxxx&password=xxxxxxxx&grant_type=password" 
["content_type"]=> string(30) "application/json;charset=UTF-8" 
["http_code"]=> int(200) 
["header_size"]=> int(669) 
["request_size"]=> int(396) 
["filetime"]=> int(-1) 
["ssl_verify_result"]=> int(0) 
["redirect_count"]=> int(0) 
["total_time"]=> float(0.602597) 
["namelookup_time"]=> float(0.028403) 
["connect_time"]=> float(0.097796) 
["pretransfer_time"]=> float(0.241315) 
["size_upload"]=> float(0) 
["size_download"]=> float(361) 
["speed_download"]=> float(599) 
["speed_upload"]=> float(0) 
["download_content_length"]=> float(-1) 
["upload_content_length"]=> float(-1) 
["starttransfer_time"]=> float(0.601087) 
["redirect_time"]=> float(0) 
["redirect_url"]=> string(0) "" 
["primary_ip"]=> string(14) "xx.xx.xx.xx" 
["certinfo"]=> array(0) { } 
["primary_port"]=> int(443) 
["local_ip"]=> string(11) "xx.xx.xx.xx" 
["local_port"]=> int(45269) 
["request_header"]=> string(396) "POST /services/oauth2/token?client_id=xxxxxxxxxxx&client_secret=xxxxxxxxx&username=xxxxxx&password=xxxxxx&grant_type=password HTTP/1.1 Host: test.salesforce.com Accept: */* Content-Type: application/x-www-form-urlencoded Expect: 100-continue " }

The dump from $info2 gives:

array(27) { 
["url"]=> string(47) "https://cs10.salesforce.com/services/data/v35.0" 
["content_type"]=> string(30) "application/json;charset=UTF-8" 
["http_code"]=> int(401) 
["header_size"]=> int(319) 
["request_size"]=> int(77) 
["filetime"]=> int(-1) 
["ssl_verify_result"]=> int(0) 
["redirect_count"]=> int(0) 
["total_time"]=> float(0.278246) 
["namelookup_time"]=> float(0.060541) 
["connect_time"]=> float(0.095554) 
["pretransfer_time"]=> float(0.170015) 
["size_upload"]=> float(0) 
["size_download"]=> float(75) 
["speed_download"]=> float(269) 
["speed_upload"]=> float(0) 
["download_content_length"]=> float(-1) 
["upload_content_length"]=> float(0) 
["starttransfer_time"]=> float(0.20641) 
["redirect_time"]=> float(0) 
["redirect_url"]=> string(0) "" 
["primary_ip"]=> string(12) "xx.xx.xx.xx" 
["certinfo"]=> array(0) { } 
["primary_port"]=> int(443) 
["local_ip"]=> string(11) "xx.xx.xx.xx" 
["local_port"]=> int(50733) 
["request_header"]=> string(77) "GET /services/data/v35.0 HTTP/1.1 Host: cs10.salesforce.com Accept: */* " }

Best Answer

I found the answer, I had the wrong syntax for setting the curl headers. I tried to pass the values as an associative array, but the correct way is like this:

curl_setopt($ci, CURLOPT_HTTPHEADER, array(
    'Accept: application/json',
    'Authorization:' . $sf_auth,
    'X-PrettyPrint:1'
));
Related Topic