ChaosMonkey ChaosMonkey - 1 month ago 68
C# Question

Error with SalesForce authentication from C# Client(s)

I am new to SalesForce and am working through getting up to speed with the basics as we will be beginning a large integration project next week. For at least part of our integration we will need to access the SalesForce Rest API. To that end another gentleman on my team and I have done some basic prototyping of calls using Postman with good success, in particular around OAuth2 authentication. Our problem is that while everything seems to work fine in Postman, as soon as we switch to using C# to make the calls we begin getting an error instead of our auth token. The response is HTTP Status 400 Bad Request and the content of the response is

{"error":"invalid_grant","error_description":"authentication failure"}.


Here is a sample of some of the C# code we have tried:

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Web;

namespace SFTokenTest
{
internal class Program
{
private const string LoginUrl = "https://test.salesforce.com/services/oauth2/token";

private static void Main(string[] args)
{
FormUrlEncodedContent content = new FormUrlEncodedContent(new []
{
new KeyValuePair<string, string>("grant_type",HttpUtility.UrlEncode("password")),
new KeyValuePair<string, string>("password",HttpUtility.UrlEncode("PASSWORD")),
new KeyValuePair<string, string>("username", HttpUtility.UrlEncode("USERNAME")),
new KeyValuePair<string, string>("client_id",HttpUtility.UrlEncode("CLIENTID")),
new KeyValuePair<string, string>("client_secret",HttpUtility.UrlEncode("CLIENTSECRET"))
});
HttpResponseMessage response;
using (HttpClient client = new HttpClient())
{
response = client.PostAsync(LoginUrl, content).Result;
}

Console.WriteLine(response.Content.ReadAsStringAsync().Result);
Console.WriteLine(response.StatusCode);

Console.ReadLine();
}
}
}


Note: this is one of many implementations we have tried including using HttpWebRequest and WebClient, all with the same result.

In addition to our own code we have tried pulling code from the GitHub repository developerforce/Force.com-Toolkit-for-NET which also presented the same issue.

So far we are two days into troubleshooting this with no luck and we have stumped our SalesForce consultant as to why it is not working. We tried resetting the Security Token this afternoon to no avail as well. I have looked through a number of articles online (including on StackOverflow) most of which point to the following issues:


  • Bad Credentials - We are using the same credentials successfully in Postman that we are attempting to use in our C# application(s) and we have tried re-coping and pasting them from the SalesForce UI and we have tried hand keying them into our code in case we were picking up invisible characters. (By credentials I am including username, password, client_id, and client secret.)

  • Bad or missing Content-Type header - we are definitely sending "application/x-www-form-urlencoded" which I have checked in each application a number of times (and have sniffed to verify it was actually being sent).

  • Unencoded Parameters - I have seen this on a number of StackOverflow threads and have verified that we are encoding the parameters we are sending I the request body.



It seems there is something that Postman is doing that we are not in our requests, but I cannot seem to pinpoint what it is. We even captured requests from both Postman and one of our clients and diffed them and they came back as identical.

Any help would be greatly appreciated.

Answer

If you haven't solved this yet, I think this might be your issue. So according to this: https://help.salesforce.com/apex/HTViewSolution?id=000221207 Starting from June 2016 Salesforce will is disabling TLS 1.0 encryption accross its whole platform in a phased approach.

If you are on any version prior to .net 4.6, it means that you won't have TLS 1.1/1,2 switched on by default. There is a little list here of options you can use to enable it: https://help.salesforce.com/apex/HTViewSolution?id=000221207#Inboundintegrations

For a console application like yours, just tested on my side and the following worked for me (on .net 4.5.2):

        string LoginUrl = "https://login.salesforce.com/services/oauth2/token";
        //the line below enables TLS1.1 and TLS1.2
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
        FormUrlEncodedContent content = new FormUrlEncodedContent(new[]
        {
        new KeyValuePair<string, string>("grant_type", "password"),
        new KeyValuePair<string, string>("client_id","CLIENTID"),
        new KeyValuePair<string, string>("client_secret","CLIENTSECRET"),
        new KeyValuePair<string, string>("password","PASSWORD + SECURITYTOKEN"),
        new KeyValuePair<string, string>("username", "USERNAME")
        });
        HttpResponseMessage response;
        using (HttpClient client = new HttpClient())
        {
            response = client.PostAsync(LoginUrl, content).Result;
        }
        Console.WriteLine(response.Content.ReadAsStringAsync().Result);
        Console.WriteLine(response.StatusCode);
        Console.ReadLine();