Thursday, December 5, 2013

Command line access to force.com #2

In the previous post I showed how to use cURL to login to salesforce.com and execute a query. It required creating a Connected App so you can use OAuth2 to authenticate and authorize. I was immediately asked by a few folks around here about the need to do that and if there was a simpler way to do this. So the good news is, yes, you can use the SOAP API to login and then use the session Id as the OAuth2 token -- it's perfectly fine.

Here is how you would issue a SOAP login request to salesforce.com using cURL without the need to configure anything:

curl -X POST https://test.salesforce.com/services/Soap/u/29.0
       -H "Content-Type:text/xml"
       -H "SOAPAction: login"
       -d "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:urn=\"urn:partner.soap.sforce.com\"><soapenv:Header/><soapenv:Body><urn:login><urn:username>YOUR_USERNAME_HERE</urn:username><urn:password>YOUR_PASSWORD_PLUS_SECURITY_TOKEN_HERE</urn:password></urn:login></soapenv:Body></soapenv:Envelope>"

Note that the above command must be in a single line. 

Now you should get a response back in XML with a bunch of details and among them the key fields to grab are the serverUrl and sessionId. The serverUrl will be of the form: https://XYZ.cs12.my.salesforce.com/services/Soap/u/29.0/00DV000000YYYYY
Just use the hostname part of the url and append /services/data/v29.0/query?q=your_query along with the sessionId as the Bearer token and you are good to go. The same query command as in the previous post should work fine with these values:

curl https://XYZ.cs12.my.salesforce.com/services/data/v29.0/query?q=Select+Name+From+Account+Limit+5 -H "Authorization: Bearer 00DV000000XXXXX!ARsAQNuDPT9Bhz9FPQFf.DZEUqATv7qbBIFc60YBOy3dvQEVEGYq4Q6iO379NYL5oeWW5yWGeBMfvrUYtyoYYYYYYY.PYFK"

The results are returned in JSON format, exactly like before!

Have fun!

Command line access to force.com

If you come from a Unix background you will surely miss the good ol' days of piping input and output between the different commands and seeing magic happen. In today's cloud connected world most of us end up working with web services and hardly do any command line anymore. There are still perfectly good use cases for using them though, especially for scripting and automating a few steps and in the case of force.com, doing deployments.

A few days ago I saw a post on developer.force.com blogs about the command line interface to force.com and I got all excited to try it out. It worked great for most part and I haven't played around with all the commands or even enough to script out a flow for specific use cases. However, the one thing that did catch me by surprise was the way authentication was done -- it uses a local HTTP server and connects to a web app hosted in heroku -- I didn't fully understand what was going on and I'm not a security expert to weigh in on the merits of this solution but I was wondering why it couldn't be done simply using OAuth2 Username/Password flow. I would have to brush up on Go programming language to perhaps extend the actual code (gladly, it's open source and on github, thank you!) but in the mean time I wanted to show how to authenticate and execute commands from the command line using cURL.

You would have to setup a Connected App as explained here. Once you set it up, you would have a Consumer Key and Consumer Secret. The next step is to get OAuth2 Access Token and for that issue the following cURL command (use test.salesforce.com for sandbox and login.salesforce.com for production or developer edition) substituting the appropriate values:

curl -X POST https://test.salesforce.com/services/oauth2/token
    -d "grant_type=password"
    -d "client_id=your_consumer_key"
    -d "client_secret=your_consumer_secret"
    -d "username=your_username"
    -d "password=your_password_plus_security_token"

Note that the above command must be issued in a single line and is shown in separate lines for clarity. 

Now SFDC should authenticate, authorize and provide the response in JSON format as follows (actual values changed for obvious reasons):

{
  "id":"https://test.salesforce.com/id/00DV00000087XXXXXX/00530000007stYYYYY",
  "issued_at":"1386263432924",
  "instance_url":"https://XYZ.cs12.my.salesforce.com",
  "signature":"WLituc1zj2tSwN7F77p1LY8slJBbGtE2kWb1mJ9H12k=",
  "access_token":"00DV00000087XXX!ARsAQNsi.0NW.mgEzP8ok_Pb.dQVam6x0ZS.1GQkjSfkTF5K"
}

The two key pieces of information that we need from the response are the instance_url and the access_token. The instance_url provides the actual sandbox (or production) instance to use and the access_token is the Session ID. Now you can make subsequent calls as follows: 

curl https://XYZ.cs12.my.salesforce.com/services/data/v29.0/query?q=Select+Name+From+Account+Limit+5 -H "Authorization: Bearer 00DV00000087XXX!ARsAQNsi.0NW.mgEzP8ok_Pb.dQVam6x0ZS.1GQkjSfkTF5K"

The Authorization header should include the access_token as the Bearer token. This request should now return the first five accounts in JSON format as follows: 

{
  "totalSize":5,
  "done":true,
  "records":[
{
             "attributes":{"type":"Account",
                             "url":"/services/data/v29.0/sobjects/Account/001V000000EWXXXYY1"},
             "Name":"Test Account #1"
        },
{
             "attributes":{"type":"Account",
                            "url":"/services/data/v29.0/sobjects/Account/001V000000EWXXXYY2"},
             "Name":"Test Account #2"
        },
{
             "attributes":{"type":"Account",
                             "url":"/services/data/v29.0/sobjects/Account/001V000000EWXXXYY3"},
             "Name":"Test Account #3"
        },
{
              "attributes":{"type":"Account",
                             "url":"/services/data/v29.0/sobjects/Account/001V000000EWXXXYY4"},
              "Name":"Test Account #4"
        },
{
             "attributes":{"type":"Account",
                            "url":"/services/data/v29.0/sobjects/Account/001V000000EWXXXYY5"},
             "Name":"Test Account #5"
        }
  ]
}

The OAuth2 access token is essentially your Session ID and should have the same validity as the Session Timeout for the Org. Also note that you don't get a refresh_token when using the username/password flow, so you would have to re-login when the session expires. 

UPDATE: Added a simpler approach in the next post to login using the SOAP API which doesn't require creation of the Connected App.

Enjoy!