I am having trouble replicating a cUrl statement in .NET. I am working against an Oauth API (ORICD SSO), and the following curl statement worked perfectly.
curl -i -L -H "Accept: application/json" --data "client_id=APP-XXXXXXXXXXXXXXXX&client_secret=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&grant_type=authorization_code&code=VicdvK&redirect_uri=https%3a%2f%2ftst.dev.local%2fsite%2fssocallback.aspx" "https://sandbox.orcid.org/oauth/token"
I get what I need from the API. However, I can't seem to replicate that success in C#. It keeps failing with a 401 Unauthorized. In Method 1 I tried sending the data as JSON.
var postdata = new Dictionary<string, string>()
{
{ "client_id", Context.Settings.ClientId },
{ "client_secret", Context.Settings.ClientSecret },
{ "grant_type", Context.Settings.GrantType },
{ "code", Context.HttpContext.Request.Params["code"] },
{ "redirect_uri", Context.Settings.RedirectUri },
};
var jsonContent = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(postdata), Encoding.UTF8, "application/json");
var response = (new HttpClient())
.PostAsync(Context.Settings.TokenUrl, jsonContent)
.Result;
In Method 2 I tried using FormUrlEncodedContent and changed it up with the client to set the accept headers.
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("client_id", Context.Settings.ClientId),
new KeyValuePair<string, string>("client_secret", Context.Settings.ClientSecret),
new KeyValuePair<string, string>("grant_type", Context.Settings.GrantType),
new KeyValuePair<string, string>("code", Context.HttpContext.Request.Params["code"]),
new KeyValuePair<string, string>("redirect_uri", Context.Settings.RedirectUri)
});
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var response = client.PostAsync(Context.Settings.TokenUrl, formContent).Result;
When that did not work, for Method 3 I built the data string manually so that it would look exactly like the curl statement from the ORCID page. I also changed up the client again, thinking that maybe there were some default accept headers messing things up.
var sb = new StringBuilder();
sb.Append("client_id=");
sb.Append(HttpUtility.UrlEncode(Context.Settings.ClientId));
sb.Append("&client_secret=");
sb.Append(HttpUtility.UrlEncode(Context.Settings.ClientSecret));
sb.Append("&grant_type=");
sb.Append(HttpUtility.UrlEncode(Context.Settings.GrantType));
sb.Append("&code=");
sb.Append(HttpUtility.UrlEncode(Context.HttpContext.Request.Params["code"]));
sb.Append("&redirect_uri=");
sb.Append(HttpUtility.UrlEncode(Context.Settings.RedirectUri));
var stringContent = new StringContent(sb.ToString(), Encoding.UTF8, "application/json");
HttpResponseMessage response;
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
using (var request = new HttpRequestMessage(new HttpMethod("POST"), Context.Settings.TokenUrl))
{
request.Content = stringContent;
response = client.SendAsync(request).Result;
}
}
All of these methods have failed. After option 3 failed, I tested with the curl statement above and it worked! The data in the curl statement above was ripped directly from the immediate window in Visual Studio from the String Builder value, so I know for sure that both of them are using the same, correct creds.
I do not know what else to test.
Note that the website I am working on is currently hosted on the same Windows 10 laptop that I ran the curl statement on.
I also tried watching the out going call with Fiddler, but it didn't pick up the POST call from my local IIS server. It would only show the browser redirects, but not the post from code.
I figured it out. It was close to Method 2:
HttpResponseMessage response;
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json");
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("client_id", Context.Settings.ClientId),
new KeyValuePair<string, string>("client_secret", Context.Settings.ClientSecret),
new KeyValuePair<string, string>("grant_type", Context.Settings.GrantType),
new KeyValuePair<string, string>("code", Context.HttpContext.Request.Params["code"]),
new KeyValuePair<string, string>("redirect_uri", Context.Settings.RedirectUri)
});
response = client.PostAsync(Context.Settings.TokenUrl, formContent).Result;
}
By default, the header for the FormUrlEncodedContent
is "Content-type", "application/x-www-form-urlencoded"
which is what it needed to be. Unfortunately, the documentation didn't clarify that, and I did not understand it.. until now.