6

We are trying to interface with our SalesForce instance via HTTP. The idea would be to call some of our custom classes from outside salesforce but without any user interaction. Typical scenario is when our ERP System calls out to SalesForce to inform it about a new customer or order.

The plan is for our ERP to call something like GET https://<our.salesforce.com>/our/res/class?param1=A&param2=B (or POST with DATA).

I am reasonably well versed in making the actual REST classes but stumped when it comes to OAuth authentication. I take it Basic Authentication by encoding the username:password as Authorization: Basic blahblah== is out? So how to authenticate or get a Session ID?

Note, this is machine-to-machine communication with no user interaction. Yet the Remote Application setup in SalesForce requires me to enter a "Callback URL" which is described as "typically the URL that a user’s browser is redirected to after successful authentication". So what is atypical? What must I enter here?

UPDATE

Here the CURL command I am using:

curl --form client_id=***** --form client_secret=***** --form grant_type=password --form username=blah%40blah.com.sandbox --form password=passwordTOKEN -H "content-type: application/x-www-form-urlencoded" https://test.salesforce.com/services/oauth2/token 

Request:

POST https://test.salesforce.com/services/oauth2/token HTTP/1.1 User-Agent: curl/7.26.0 Host: test.salesforce.com Accept: */* Content-Length: 723 Expect: 100-continue content-type: application/x-www-form-urlencoded; boundary=----------------------------e3e12e44c894 ------------------------------e3e12e44c894 Content-Disposition: form-data; name="client_id" ***** ------------------------------e3e12e44c894 Content-Disposition: form-data; name="client_secret" ***** ------------------------------e3e12e44c894 Content-Disposition: form-data; name="grant_type" password ------------------------------e3e12e44c894 Content-Disposition: form-data; name="username" blah%40blah.com.dev1 ------------------------------e3e12e44c894 Content-Disposition: form-data; name="password" passwordTOKEN ------------------------------e3e12e44c894-- 

Response:

HTTP/1.1 400 Bad Request Date: Tue, 28 Jan 2014 13:20:53 GMT Pragma: no-cache Cache-Control: no-cache, no-store Content-Type: application/json;charset=UTF-8 Content-Length: 81 {"error_description":"grant type not supported","error":"unsupported_grant_type"} 

Tried using test.salesforce.com and instance.salesforce.com (which the docs use). Also tried with and without API token appended to password.

My client_id is 85 characters long.

7
  • Can your ERP append HTTP headers to the outgoing requests? Commented Jan 28, 2014 at 11:37
  • Sure, of course. Commented Jan 28, 2014 at 12:11
  • 1
    Needless to say unsupported_grant_type is not one of the documented error codes! help.salesforce.com/apex/… Commented Jan 28, 2014 at 13:42
  • you have to use login.salesforce if are using production or test.salesforce if are using a sandbox. That error is typical when you are missing a header or have a wrong encoded value. stackoverflow.com/questions/10890467/… Commented Jan 28, 2014 at 13:45
  • I'm using test.salesforce.com as I'm in a sandbox. I've also 100% verified my credentials (username, password and token) into the same instance by using the SOAP API (test.salesforce.com/services/Soap/c/24.0) Commented Jan 28, 2014 at 13:46

3 Answers 3

6

You must use OAth login flow first to have REST access. There is a lot of good information out there about Salesforce OAuth login flow.

In order to make it programatically (Server to Server ), I think that the most efficient way is:

Steps

  1. Set the current access_token to the request header you need to do.
  2. Make the request trying to catch any exception.
  3. If you have an exception of type 401 Unauthorized you have to launch the login flow and save the access_token for future requests.

Using this you don't have to go through the login flow on each request.

Login

This is a sample of how to get the access_token:

Request

Realize an HTTP POST to https://[login].salesforce.com/services/oauth2/token **you must select between test or login according the environment we want to use (sandbox or production).

Body:

client_id=CLIENT-ID &client_secret=CLIENTE-SECRET &password=PASSWORD+SECURITY-TOKEN &username=USERNAME &grant_type=password 

** replace default values with values from your org/user
Headers:

content-tpe: application/x-www-form-urlencoded 

Answer = Ok. Ex:

{ "id":"https://login.salesforce.com/id/XXXX/XXXX", "issued_at":"1378371927512", "instance_url":"https://eu2.salesforce.com", "signature":"XXXXXXX=", "access_token":"_Z_ZZZZZZZZZZ" } 

Answer = Error Ex:

{ "error":"invalid_grant", "error_description":"authentication failure - Invalid Password" } 

WS Usage

Once obtained the access_token and instance_url, you can send the requests to the web services

More information

You will found a deeper information here about external applications on REST

6
  • 2
    I've tried your proposal (see my 'Answer' update) but get only 400 errors. The question remains: in order to setup OAuth I must enter a "Callback URL". What is that? Commented Jan 28, 2014 at 12:50
  • You must add the TOKEN and also check the header content-type. Set the callback using localhost, that is used in other flows. Commented Jan 28, 2014 at 12:54
  • 2
    I've tried the password with and without the token. Also with and without the content-type header. Also using a vairety of callback URLs. Always get grant type not supported. btw. Typo: content-tpe Commented Jan 28, 2014 at 13:14
  • 1
    Your answer is correct in theory just doesn't work for me in practice. If I do exactly the same HTTP call from APEX code it works perfectly. I've tried whitelisting my IP (CURL client) to no avail. Commented Jan 28, 2014 at 14:00
  • Your question has changed. You first ask about what authentication you should implement, now you are asking about an error you are getting. Probably, the better way the community can help you is if you create a new question about the error. Commented Jan 28, 2014 at 14:16
0

Finally got this working (i.e. solved the enigmatic unsupported_grant_type error) though the reason remains unclear. It seems (my version of) CURL creates a different kind of request to "normal" HTTP clients which the REST endpoint is not understanding.

When I compared the POST with what apex HttpRequest() was doing I noticed CURL adds this header:

content-type: application/x-www-form-urlencoded; boundary=----...etc. 

and then have a complex body using that boundary whilst other clients do without it:

content-type: application/x-www-form-urlencoded

and have a simple body:

grant_type=password&client_id=.... 

Thanks a lot CURL, think I'll stick to Fiddler from now on.

So HTTP Request:

POST /services/oauth2/token HTTP/1.1 User-Agent: Fiddler Host: test.salesforce.com Accept: */* Content-Length: 241 content-type: application/x-www-form-urlencoded grant_type=password&client_id=***.***.***&client_secret=***&username=blah%40blah.com.dev1&password=blahTOKEN 

Get's you:

HTTP/1.1 200 OK 

etc.

1
  • your problem was that you were using --form option to your parameters, they specify ` --form CONTENT Specify HTTP multipart POST data (H), so curl actually created added body parts which probably doesn't make sense, as content type is not multipart, and so it added boundary` to header. In order to make it work in curl, you need to use -d instead of --form Commented Aug 12, 2015 at 15:48
0

You can use Username-Password OAuth Authentication Flow. The username-password authentication flow can be used to authenticate when the consumer already has the user’s credentials. In this flow, the user’s credentials are used by the application to request an access token as shown in the following steps.

enter image description here

An example request body might look something like the following:

grant_type=password&client_id=3MVG9lKcPoNINVBIPJjdw1J9LLM82Hn FVVX19KY1uA5mu0QqEWhqKpoW3svG3XHrXDiCQjK1mdgAvhCscA9GE&client_secret= 1955279925675241571&username=testuser%40salesforce.com&password=mypassword123456 
4
  • So I need to use OAuth? Which means I need to enter a callback URL (or I can't enable OAuth)? See also my update (400 Error). Commented Jan 28, 2014 at 12:15
  • You can use any callback url when using this flow with OAuth Commented Jan 28, 2014 at 12:31
  • There's some info on salesforce.stackexchange.com/questions/22704/… answer that might help Commented Jan 28, 2014 at 12:36
  • Phil, that seems to deal with remote sites as in sites that my SF instance wants to call out to. Commented Jan 28, 2014 at 13:26

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.