Andrew Connell [MVP SharePoint]
1741 Posts |  46 Articles |  5310 Comments
.NET  |  MCMS  |  SharePoint  |  Office System
Andrew Connell's RSS Feed    
AC's Blog Quick Links
SharePoint Quick Links
Article Categories
Archives
May, 2012 (3)
April, 2012 (4)
March, 2012 (4)
February, 2012 (4)
January, 2012 (12)
December, 2011 (6)
November, 2011 (3)
October, 2011 (17)
September, 2011 (8)
August, 2011 (6)
July, 2011 (7)
June, 2011 (13)
May, 2011 (9)
April, 2011 (15)
March, 2011 (1)
February, 2011 (6)
January, 2011 (5)
December, 2010 (11)
November, 2010 (6)
October, 2010 (12)
September, 2010 (5)
August, 2010 (4)
July, 2010 (5)
June, 2010 (6)
May, 2010 (11)
April, 2010 (11)
March, 2010 (9)
February, 2010 (9)
January, 2010 (3)
December, 2009 (10)
November, 2009 (15)
October, 2009 (15)
September, 2009 (7)
August, 2009 (4)
July, 2009 (10)
June, 2009 (8)
May, 2009 (2)
April, 2009 (9)
March, 2009 (6)
February, 2009 (16)
January, 2009 (6)
December, 2008 (12)
November, 2008 (12)
October, 2008 (27)
September, 2008 (13)
August, 2008 (14)
July, 2008 (14)
June, 2008 (12)
May, 2008 (23)
April, 2008 (12)
March, 2008 (15)
February, 2008 (13)
January, 2008 (12)
December, 2007 (10)
November, 2007 (8)
October, 2007 (15)
September, 2007 (20)
August, 2007 (21)
July, 2007 (16)
June, 2007 (8)
May, 2007 (25)
April, 2007 (16)
March, 2007 (18)
February, 2007 (18)
January, 2007 (12)
December, 2006 (16)
November, 2006 (13)
October, 2006 (18)
September, 2006 (22)
August, 2006 (27)
July, 2006 (23)
June, 2006 (23)
May, 2006 (23)
April, 2006 (9)
March, 2006 (17)
February, 2006 (15)
January, 2006 (23)
December, 2005 (31)
November, 2005 (32)
October, 2005 (38)
September, 2005 (53)
August, 2005 (30)
July, 2005 (63)
June, 2005 (30)
May, 2005 (59)
April, 2005 (29)
March, 2005 (74)
February, 2005 (27)
January, 2005 (22)
December, 2004 (32)
November, 2004 (42)
October, 2004 (39)
September, 2004 (20)
August, 2004 (14)
July, 2004 (27)
June, 2004 (40)
May, 2004 (5)
April, 2004 (6)
March, 2004 (16)
February, 2004 (26)
January, 2004 (23)
December, 2003 (7)
November, 2003 (14)
October, 2003 (20)
September, 2003 (4)
Post Categories



Managed Windows Shared Hosting

In the past I’ve complained about the Salesforce SOAP API for reading/writing your CRM databases. Last year Salesforce added a new API, one founded on RESTful principals, for developers to work with. I wish it was implemented as an OData service, but I guess the Salesforce guys are stuck behind the times.

At any rate, I had a need to read/write to a Salesforce instance and couldn’t bear to use their SOAP API again so I decided to give the REST API a go. Their developer site doesn’t do that much in the way of help for .NET developers (there is some, but not much)… everything is really skewed towards PHP or their own APEX language. After some searching I found Dan Boris’ series of Salesforce related posts. These are good and he covers a lot of good things such as:

Dan’s posts talk about using the method of authentication where you create an app that contains an embedded browser control and you get the user using t your app to login to Salesforce. Then your app grabs the OAuth token and include that with all future requests. Before you do this you need to configure a new remote access app for your Salesforce account. This is done by logging in and going to SETUP > DEVELOP > REMOTE ACCESS. Click NEW and create a new remote access app. One of the fields it will give you is a callback URL that is required. This is used by Salesforce to redirect the user back to a specific URL after a successful login. In a web app this makes sense but not if you want to use this within a console app or a service. What I do is put just a dummy URN in there that I’ll look for later like resttest:callback.

Now, Dan’s stuff works great in many cases, but not if you want to create an app that runs like a service and automatically logs in and does some stuff with the API without user interaction. The way you authenticate with Salesforce is using OAuth 2.0. OAuth 2.0 supports multiple authentication flows and Salesforce supports many of them as outlined in their SDK: Authenticating Remote Access Application OAuth & discussed on their DeveloperForce site: Digging Deeper into OAuth 2.0 on Force.com. In my scenario I want to focus on creating an app that runs as a service to use the REST API without any user action required for logging in. This is the least secure because it means I have to store a username & password somewhere, but that's just the ante for playing the game. So, in this case I want to use the OAuth 2.0 Username & Password Flow.

So, to authenticate you do something like this using the OAuth 2.0 Username & Password flow:

   1: private void AuthenticateToSalesforce(){
   2:   string uri = "https://login.salesforce.com/services/oauth2/token";
   3:  
   4:   var webClient = new WebClient() {
   5:     BaseAddress = uri
   6:   };
   7:  
   8:   var collection = new NameValueCollection();
   9:   collection.Add("grant_type", "password");
  10:   collection.Add("client_id", CONSUMER_KEY);
  11:   collection.Add("client_secret", CONSUMER_SECRET);
  12:   collection.Add("username", SALESFORCE_USERNAME);
  13:   collection.Add("password", SALESFORCE_PASSWORD_AND_SECRETKEY);
  14:  
  15:   byte[] responseBytes = webClient.UploadValues("", "POST", collection);
  16:   string response = Encoding.UTF8.GetString(responseBytes);
  17:   string decodedResponse = HttpUtility.UrlDecode(response);
  18:   JavaScriptSerializer js = new JavaScriptSerializer();
  19:   var token = js.Deserialize<TokenResponse>(decodedResponse);
  20: }
  21:  
  22: public class TokenResponse {
  23:   public string id { get; set; }
  24:   public string issued_at { get; set; }
  25:   public string refresh_token { get; set; }
  26:   public string instance_url { get; set; }
  27:   public string signature { get; set; }
  28:   public string access_token { get; set; }
  29: }

As you can see I’m creating a Web request and passing in a handful of things in the header. Specifically I’m passing in the grant_type of “password” to signal I want to use the OAuth 2.0 Username & Password flow. I’m also passing in the consumer key & secret you get from creating the remote access app in your Salesforce account and finally the username & password to authenticate with. This will return a JSON array (you can also get a XML response if you like) which I deserialize into a custom object that will pull everything I need. Specifically I need the instance_url (which is the URL of my Salesforce account) and the access_token which is what you need to include in all future requests.

Once you have the token, you can use the REST API to read & write data like Dan shows in his posts I linked to above, or like this:

   1: public void QuerySalesforce(){
   2:   // build LEAD query
   3:   XDocument xDoc = XDocument.Load(@"SalesForceSchema.xml");
   4:   var fields = from x in xDoc.Root.Element("Lead").Elements("Field") 
   5:                select x.Attribute("SfName").Value;
   6:   string query = String.Format("SELECT {0} FROM Lead 
   7:                                 WHERE Email = 'something@email.com'"
   8:                                , string.Join(",", fields));
   9:  
  10:   string sfQuery = String.Format("{0}/services/data/v23.0/query?q={1}",
  11:                    token.instance_url,
  12:                    query);
  13:   HttpWebRequest request = HttpWebRequest.Create(sfQuery) as HttpWebRequest;
  14:   request.Headers.Add("Authorization", "OAuth " + token.access_token);
  15:   request.ContentType = "application/json";
  16:   request.Method = "GET";
  17:  
  18:   WebResponse webResponse = request.GetResponse();
  19:   var sr = new System.IO.StreamReader(webResponse.GetResponseStream());
  20:   string json = sr.ReadToEnd();
  21:   var leads = js.Deserialize<sfdcLeadCollection<sfdcLeadForCollection>>
  22:                                     (HttpUtility.UrlDecode(json));
  23: }
  24:  
  25: public class sfdcLeadCollection<T> {
  26:   public bool Done { get; set; }
  27:   public int TotalSize { get; set; }
  28:   public string nextRecordsUrl { get; set; }
  29:   public List<T> Records { get; set; }
  30: }
  31:  
  32: public class sfdcAttributes {
  33:   public string Type { get; set; }
  34:   public string Url { get; set; }
  35: }
  36:  
  37: public class sfdcLeadForCollection : sObjectLead {
  38:   public sfdcAttributes Attributes { get; set; }
  39: }
  40:  
  41: public class sObjectLead {
  42:   public string Id { get; set; }
  43:   public string Email { get; set; }
  44:   public string FirstName { get; set; }
  45:   public string LastName { get; set; }
  46:   public string Company { get; set; }
  47:   public string Phone { get; set; }
  48:   public string MobilePhone { get; set; }
  49:   public string Fax { get; set; }
  50:   public string Website { get; set; }
  51:   public string Street { get; set; }
  52:   public string City { get; set; }
  53:   public string State { get; set; }
  54:   public string Country { get; set; }
  55:   public string PostalCode { get; set; }
  56: }
Technorati Tags: ,
posted on Wednesday, February 01, 2012 6:49 AM

Feedback

# re: Authenticating & Working with the Salesforce REST API & OAuth 3/21/2012 12:46 PM Basudeb Chaterjee
Gravatar Great post...I was trying to get this stuff to work for a while. One question: When you set up the remote application in Salesforce in order to get the key and consumer secret, you have to specify a callback URL. Does that URL matter for that initial request you're sending? Or do you always get a valid response back that you can parse out to get your access token for subsequent requests?

Post Feedback

Title:
Name:
Email:
(email will not be displayed)
Url:
Comments: 
Please add 6 and 4 and type the answer here:    
All Comments Are Filtered & Moderated
Unfortunately comment spammers are just too effecient and are constantly dirtying up blogs with irrelevant and unwanted comments trying to improve their standing on search engines. All comments on this blog are moderated. I do not censor comments, but I don't approve comments with vulger language or those soliciting products. Most of the time comments are approved within a few hours of being submitted with the only exception when I'm traveling.

Why are you asking for my email address?
The only reason I'm asking for your email address, which isn't required to submit a comment, is to provide a gravatar if you've created an account for yourself and associated your email address with a small image. If you have a gravatar created for the email address you submit, it will appear next to your comment. Otherwise nothing will appear.

What is a gravatar?
A gravatar is a "globally recognized avatar." You can get more information about gravatars, as well as create your own for free, at www.gravatar.com. You can also view my gravatar here.


Copyright © 2003 - 2012 Andrew Connell
Creative Commons License 
This work is licensed under a Creative Commons License

 
SharePoint Training
Looking for SharePoint 2010 training for developers, administrators, power users, information workers, end users & web designers? Look no further! My company, Critical Path Training offers the best SharePoint training around! We offer public & private classes both as in-person instructor-loed hands-on classes and online classes. Check out our schedule and course catalog for all the ways we can get you going on your SharePoint path!