Thursday, December 5, 2013

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!


1 comment: