Introduction

This article is an extension of the article Calling the Force.com REST API from BizTalk Server posted by one of our BizTalk Gurus, Richard Seroter. A small change has been made to the shared code to accommodate a Force.com multi-endpoints scenario.

Problem

When we have to call multiple Endpoints of SalesForce REST API from BizTalk Server, we mostly get API error: INVALID_SESSION_ID.

Root Cause

As explained in the above article, we use five credential values to authenticate any REST API Endpoint Address:
  1. Consumer Key
  2. Consumer Secret
  3. Password
  4. Security Token
  5. Username




Imagine a scenario in which we have two different Endpoint Addresses with different credentials, (suppose: https://ns17.salesforce.com/services/data/v25.0/ and https://ns18.salesforce.com/services/data/v25.0/) and we want to integrate with both in two separate BizTalk Applications (suppose: App1 and App2). We have setup the WCF-WebHTTP ports in both the applications accordingly.

That will result in the following:
  1. App1 calls the REST API.
    Result: A new token will be generated ("New SFDC token acquired"). Success.
  2. App1 calls the REST API again within 60 Minutes of the last call.
    Result: Existing token will be used ("Existing SFDC token returned"). Success.
  3. App2 calls the REST API within 60 Minutes of the last call by App1.
    Result: Existing token will be used ("Existing SFDC token returned") Error: INVALID_SESSION_ID
The Error is of course a bit of a problem.  That happens because App2 has used the token App1 supplied, which is invalid for the App2 Endpoint. A similar problem will happen with App1 calls when it tries to use App2 token.

The problem is in the below portion of the code where we are not getting a new token when the elapsed time since the token was accessed is less than or equal to 60 minutes:

public static class SfdcTokenManager
    {
        private static DateTime _sessionLastAccessDate = DateTime.Now;
        private static string _sessionId = string.Empty;
 
        public static string GetSession(string oauthkey, string oauthsecret, string uname, string password, string token)
        {
            //get current date time
            DateTime now = DateTime.Now;
            //get the difference from the last time the token was accessed
            TimeSpan diff = now.Subtract(_sessionLastAccessDate);
 
            //if this is the first time we're calling class, or the token is stale, get a new token
            if (_sessionId == string.Empty || (diff.TotalMinutes >= 60))
            {
                //refresh token
 
                try
                {
                    HttpClient authClient = new HttpClient();
 
                    //create login password value
                    string loginPassword = password + token;
 
                    //create payload consisting of required values
                    HttpContent content = new FormUrlEncodedContent(new Dictionary<string,string>
                {
                    {"grant_type","password"},
                    {"client_id",oauthkey},
                    {"client_secret",oauthsecret},
                    {"username",uname},
                    {"password",loginPassword}
                }
                   );
 
                    //issue request to the Force.com login endpoint
                 
                    //var response = authClient.PostAsync("https://login.salesforce.com/services/oauth2/token", content);
                    var response = authClient.PostAsync("https://test.salesforce.com/services/oauth2/token", content);
                    if (response.Result.IsSuccessStatusCode)
                    {
                        //get the result
                        var responseContent = response.Result.Content;
                        string responseString = responseContent.ReadAsStringAsync().Result;
 
                        //load result into JSON object
                        JObject obj = JObject.Parse(responseString);
                        //extract the access token
                        _sessionId = (string)obj["access_token"];
 
                        System.Diagnostics.EventLog.WriteEntry("Application", "New SFDC token acquired");
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.EventLog.WriteEntry("Application", "Error getting token: " + ex.ToString());
                }
            }
            else
            {
                System.Diagnostics.EventLog.WriteEntry("Application", "Existing SFDC token returned");
            }
 
            //update last access time since session is good for an hour since last call
            _sessionLastAccessDate = DateTime.Now;
 
            //give the session ID to the caller
            return _sessionId;
        }

Solution

There is a simple solution to this problem. We just have to add another condition to check if any credential value is different than the previous one (i.e; Username is a unique value).

We can do this using the following code:

public static class SfdcTokenManager
    {
        private static DateTime _sessionLastAccessDate = DateTime.Now;
        private static string _sessionId = string.Empty;
        private static string _uname = string.Empty;
 
        public static string GetSession(string oauthkey, string oauthsecret, string uname, string password, string token)
        {
            //get current date time
            DateTime now = DateTime.Now;
            //get the difference from the last time the token was accessed
            TimeSpan diff = now.Subtract(_sessionLastAccessDate);
 
            //if this is the first time we're calling class, or the token is stale, get a new token
            if (_sessionId == string.Empty || (diff.TotalMinutes >= 60) || uname != _uname)
            {
                //refresh token
 
                try
                {
                    HttpClient authClient = new HttpClient();
 
                    //create login password value
                    string loginPassword = password + token;
 
                    //create payload consisting of required values
                    HttpContent content = new FormUrlEncodedContent(new Dictionary<string,string>
                {
                    {"grant_type","password"},
                    {"client_id",oauthkey},
                    {"client_secret",oauthsecret},
                    {"username",uname},
                    {"password",loginPassword}
                }
                   );
 
                    //issue request to the Force.com login endpoint
                 
                    //var response = authClient.PostAsync("https://login.salesforce.com/services/oauth2/token", content);
                    var response = authClient.PostAsync("https://test.salesforce.com/services/oauth2/token", content);
                    if (response.Result.IsSuccessStatusCode)
                    {
                        //get the result
                        var responseContent = response.Result.Content;
                        string responseString = responseContent.ReadAsStringAsync().Result;
 
                        //load result into JSON object
                        JObject obj = JObject.Parse(responseString);
                        //extract the access token
                        _sessionId = (string)obj["access_token"];
 
                        System.Diagnostics.EventLog.WriteEntry("Application", "New SFDC token acquired");
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.EventLog.WriteEntry("Application", "Error getting token: " + ex.ToString());
                }
            }
            else
            {
                System.Diagnostics.EventLog.WriteEntry("Application", "Existing SFDC token returned");
            }
 
            //update last access time since session is good for an hour since last call
            _sessionLastAccessDate = DateTime.Now;
            _uname = uname;
 
            //give the session ID to the caller
            return _sessionId;
        }

Source Code

Source Code for this article can be downloaded at the MSDN Code Gallery: Calling the Force.com REST API from BizTalk Server - Multiple Endpoints.

See Also

Another important place to find an extensive amount of BizTalk related articles is the TechNet Wiki itself. The best entry point is BizTalk Server Resources on the TechNet Wiki.

 Return to Top