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.

  • Can your ERP append HTTP headers to the outgoing requests? – bigassforce Jan 28 '14 at 11:37
  • Sure, of course. – Marc Jan 28 '14 at 12:11
  • 1
    Needless to say unsupported_grant_type is not one of the documented error codes! help.salesforce.com/apex/… – Marc Jan 28 '14 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/… – Martin Borthiry Jan 28 '14 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) – Marc Jan 28 '14 at 13:46
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

  • 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? – Marc Jan 28 '14 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. – Martin Borthiry Jan 28 '14 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 – Marc Jan 28 '14 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. – Marc Jan 28 '14 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. – Martin Borthiry Jan 28 '14 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.

  • 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 – dhblah Aug 12 '15 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
  • 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). – Marc Jan 28 '14 at 12:15
  • You can use any callback url when using this flow with OAuth – Shebin Mathew Jan 28 '14 at 12:31
  • There's some info on salesforce.stackexchange.com/questions/22704/… answer that might help – Phil Hawthorn Jan 28 '14 at 12:36
  • Phil, that seems to deal with remote sites as in sites that my SF instance wants to call out to. – Marc Jan 28 '14 at 13:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.