My Connected App settings are the following:

(1) Setup->Manage Apps->Connected Apps->FirstConnectedApp->Refresh Token Policy: "Refresh token is valid until revoked"

(2) Setup->Security Controls->Session Settings->Session Timeout->Timeout Value: "15 minutes".

I can obtain a new access token (using Web Server Flow) and use it to update data using Salesforce Connected App.

Now, I go home and come back the next day to update some data using HTTPS and PATCH, and I get the following response:

        "message": "Session expired or invalid",
        "errorCode": "INVALID_SESSION_ID"

This error makes perfect sense because the session has expired. Now, I attempt to follow the "Understanding the OAuth Refresh Token Process" (https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_understanding_refresh_token_oauth.htm). I send a HTTPS POST using the following settings:

  • Header item: "Content-Type: x-www-form-urlencoded"
  • Header item: "Authorization: client_id:'Client ID/Consumer Key', client_secret:'Client Secret/Consumer Secret'"
  • Body item: "grant_type=refresh_token&refresh_token='refresh_token'"

Now, I submit this HTTPS POST and I get the following response:

    "error": "invalid_client_id",
    "error_description": "client identifier invalid"

Why would I be getting this response?

I double checked to make sure that in fact the "Client ID" matches the "Consumer Key" in my Connected App.

3 Answers 3


grant_type,client_id,client_secret and refresh_token should be in BODY instead of Request header.




The client_id and the client_secret parameters should be in the body of the request. The only parameter supported in the header is the format you'd like the response to be returned in.

From the example in the documentation you can see the parameters in the request body:

POST /services/oauth2/token HTTP/1.1
Host: https://login.salesforce.com/ 
&refresh_token=your token here

Thanks to both of you. I was able to move the client_id and client_secret to the body to solve my problem, the new 'C' source code looks like:

    pCurlSession = curl_easy_init();
    if (0 != pCurlSession)
        // Make 'refresh token' request to be ib "x-www-form-urlencoded" format
        pRequestHeader = curl_slist_append(pRequestHeader, "Content-Type: application/x-www-form-urlencoded");

        // Create the body
        char cRequestBody[1024] = { 0 };
            cRequestBody,                                                              // char * str
            "grant_type=refresh_token&client_id=%s&client_secret=%s&refresh_token=%s", // const char * format
            client_id,                                                                 // ...
            client_secret,                                                             // ...
            refresh_token                                                              // ...

        curl_easy_setopt(pCurlSession, CURLOPT_URL, "https://instance.salesforce.com/services/oauth2/token");
        curl_easy_setopt(pCurlSession, CURLOPT_HTTPHEADER, pRequestHeader);
        unsigned long uiLength = sizeof(char)*strlen(cRequestBody);
        curl_easy_setopt(pCurlSession, CURLOPT_POSTFIELDSIZE, uiLength);
        curl_easy_setopt(pCurlSession, CURLOPT_POSTFIELDS, cRequestBody);
        curl_easy_setopt(pCurlSession, CURLOPT_VERBOSE, 0L);
        curl_easy_setopt(pCurlSession, CURLOPT_WRITEFUNCTION, CURL_TEST_write_callback);

        eCurlResult = curl_easy_perform(pCurlSession);

The response is now:


I can't figure out why the access token is the same as the original I got. Would it be because my session has not expired?

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .