I am trying to get Autherization Token for TD Ameritrade API using C# .NET. First I get a "CODE" following some steps. Then I use that code in Postman, send the request I get Authorization Code back.
Here is my C# .NET Code to do the same :- (the "CODE" is one time value, can't be re-use. I get a NEW CODE and use that in C# app)
public async Task SignIn(string consumerKey, string code, string redirectUrl = "https://127.0.0.1:4000/TDCallBack/SetCode?code")
{
var path = "https://api.tdameritrade.com/v1/oauth2/token";
using (var client = new HttpClient())
{
var dict = new Dictionary<string, string>
{
{ "grant_type", "authorization_code" },
{ "access_type", "offline" },
{ "client_id", $"{consumerKey}" },
{ "redirect_uri", redirectUrl },
{ "code", code }
};
var req = new HttpRequestMessage(HttpMethod.Post, path) { Content = new FormUrlEncodedContent(dict) };
var res = await client.SendAsync(req);
}
}
But I get "Invalid_Grant" as return, which means API call went to TD Ameritrate server but it did not return the Authorization Token. It could be header/data issue, but no explanation why it did not return an Authorization token.
I think it is a C# programming issue, because TD Ameritrade has a WEB Page (like swagger) to submit API Calls. Exact same API call work with same parameters/values (of course NEW CODE every time). Postman works, TD Ameritrade Website Works BUT my C# Code doesn't work. I will appreciate if I can get some light on this problem. Thanks.
Have you tried using a proxy (like Fiddler) to see what the differences are between the Postman request and request from your code?
That should go a long way towards finding obvious differences. It also looks like you’re not setting the content type to be x-www-form-urlencoded which may be necessary for the endpoint to properly handle the request.
See also: How to POST using HTTPclient content type = application/x-www-form-urlencoded
Related
I am trying to use C# HttpClient from ASP.NET MVC to make a request to an API. My API is running on .NET 6.0.
httpClient.BaseAddress = new Uri(_url);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue($"Bearer", $"{token}");
var serialized = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");
var httpResponseMessage = await httpClient.PutAsync(urlToSend, serialized);
Here is my code. I tried all the possibilities I saw on google. But when sending request, I can't send Authorization header.
I can send it with Postman.
Here is my API code:
[Consumes("application/json")]
[Produces("application/json", "text/plain")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IResult))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(IResult))]
[HttpPut("changeuserpassword")]
public async Task<IActionResult> ChangeUserPassword([FromBody] ChangePasswordCommand changePasswordCommand)
{
var accessToken = Request.Headers[HeaderNames.Authorization];
return GetResponseOnlyResult(await Mediator.Send(changePasswordCommand));
}
Note: In my _url, I use http, not https.
I'm not sure but maybe the [AllowAnonymous]attribute remove the Authorization header from request just because it does not make sense if no authorization is needed.
Have you checked if the sent request contains the header using a tool like fiddler ?
I solved the problem by changing my base url from HTTP to HTTPS.
I tried with Fiddler and I got the same problem when I request to HTTP.
So thanks to #olivier-duhart .
To add to the accepted answer, the problem gets solved by changing from HTTP to HTTPS is due to the fact that, the Authorization header gets stripped during redirects.
This behavior is for security concerns and is by design, as mentioned in the github discussion here.
The same behavior may not be seen when using Postman vs HttpClient for example, is due to the way that different clients, have differing mechanisms, by which the subsequent requests (following a response status 30X) to the redirect location are handled.
Also a great answer elsewhere on stackoverflow : Authorization header is lost on redirect
Please review this link. Allow Anonymous will ignore the authentication header
https://github.com/dotnet/aspnetcore/issues/30546
I tried with the code. It seems working fine for me. Here is my code of console app
try
{
ChangePasswordCommand passobj = new ChangePasswordCommand() { password = "new password"};
string _url = "https://localhost:44395/api/Values/";
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(_url);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue($"Bearer", $"MYTOKEN");
var serialized = new StringContent(JsonConvert.SerializeObject(passobj), Encoding.UTF8, "application/json");
var httpResponseMessage = await httpClient.PutAsync("changeuserpassword", serialized);
}
catch (Exception ex) {
}
And here is controler Api
[AllowAnonymous]
[Consumes("application/json")]
[Produces("application/json", "text/plain")]
[HttpPut("changeuserpassword")]
public async Task<IActionResult> ChangeUserPassword(ChangePasswordCommand changePasswordCommand)
{
var accessToken = Request.Headers[HeaderNames.Authorization];
return Ok();
}
I spent some time trying to figure this one out so I decided to post it here - hopefully it saves some time to someone else.
I'm building an ASP.Net Core Web API MVC application that accepts a Get request and makes a call to an external API (in this case is the Bing Image Search). When returning a result, it would give me a escaped Json string. Example:
"{\"_type\": \"Images\", \"instrumentation\": {\"pageLoadPingUrl\": \"https:...}
Instead of:
{
"_type": "Images",
"instrumentation": {
"pageLoadPingUrl": "https:....
}
Then, I wanted to pass it back to my web client with all sort of non-successes.
I will post shortly how I solved it.
Cheers!
So the issue was that I was trying to process the reponse content the wrong way. All I had to do is user the JsonConvert library.
My full API method looks like this:
[HttpGet("{id}")]
public async Task<IActionResult> Get(string id)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "Enter your key here");
var uri = new Uri("uri to external API here + any parameters");
var response = await client.GetStringAsync(uri);
var jsonResponse = JsonConvert.DeserializeObject(response);
return Ok(jsonResponse);
}
}
Cheers! :)
I've been having a few issues in trying to retrieve the results of a POST operation from a Web Service.
I have been using a chrome extension to test the API Services and they are working there. However I've been having problems on implementing it in code.
This is an example of usage of the chrome extension:
What I'm trying to retrieve on code, is the last part, the json array that the POST operation generates, where it says accessToken.
However, in the code that I've been using below, I've only had access to the status (200 OK) etc.
Here's a preview of the code I am using:
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(url.Text);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(header.Text));
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url.Text);
request.Content = new StringContent(body.Text, Encoding.UTF8, header.Text);
client.SendAsync(request)
.ContinueWith(responseTask =>
{
MessageBox.Show(responseTask.Result.Content.Headers.ToString());
}
);
}
The Header.Text is exactly "application/json", the body.Text is body which has those various properties such as username and password (in string format) and url.Text contains the complete URL to call the Web service.
I'd like to know what I'm doing wrong with my code, and what can I do to obtain that json array that contains the accessToken
In your code you need to use ReadAsStringAsync method to convert your HttpContent object to string/json. For example:
client.SendAsync(request)
.ContinueWith(responseTask =>
{
var jsonString = responseTask.Result.Content.ReadAsStringAsync().Result;
MessageBox.Show(jsonString);
});
then you can convert you jsonString as you need.
I'm trying to make an application for Windows which acts like a server for a mobile application (PhoneGap). This application is like a remote for the server application, it invokes methods in which their turn do things.
After long searching and trying to see which components can work together I found OWIN and Web API. So I'm trying to implement this into my application, but I cannot seem to grasp how I can POST a string to invoke methods.
My thought of process is that I POST a string to the server, which it reads and with a switch statement to check the value of the string I know which method to invoke. Very simple, straightforward (not faulty proof probably), but it's a start.
But I cannot seem to get it to work. Here is my controller:
public void Post([FromBody]string value)
{
switch(value)
{
case("buttonOne"):
{
mainClass.pressButtonOne();
break;
}
}
}
I'm using HttpClient to emulate the client on the host pc:
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:6740");
var content = "buttonOne";
var result = client.PostAsync("api/values", content).Result;
string resultContent = result.Content.ReadAsStringAsync().Result;
Console.WriteLine(resultContent);
But this is getting errors, the string isn't a valid HttpContent. But it's always asking for a pair instead of single.
When making POST requests the post content should be of type HttpContent or one of its derived types.
var content = new FormUrlEncodedContent(new Dictionary<string, string> {
{"value" , "buttonOne"} })
I have an API (https://www.readability.com/developers/api/parser#idm386426118064) to extract the contents of the webapges, but on passing a shortened url or an url that redirects to other, it gives error.
I am developing windows phone 8.1 (xaml) app. Is there any way to get the destination url in c# or any work around?
eg url - http://www.bing.com/r/2/BB7Q4J4?a=1&m=EN-IN
You could intercept the Location header value before the HttpClient follows it like this:
using (var handler = new HttpClientHandler())
{
handler.AllowAutoRedirect = false;
using (var client = new HttpClient(handler))
{
var response = await client.GetAsync("shortUrl");
var longUrl = response.Headers.Location.ToString();
}
}
This solution will always be the most efficient because it only issue one request.
It is possible however, that the short url will reference another short url and consequently cause this method to fail.
An alternative solution would be to allow the HttpClient to follow the Location header value and observe the destination:
using (var client = new HttpClient())
{
var response = client.GetAsync("shortUrl").Result;
var longUrl = response.RequestMessage.RequestUri;
}
This method is both terser and more reliable than the first.
The drawback is that this code will issue two requests instead of one.
You can get the ResponseUri from GetResponse():
string redirectedURL = WebRequest.Create("http://www.bing.com/r/2/BB7Q4J4?a=1&m=EN-IN")
.GetResponse()
.ResponseUri
.ToString();
Interesting article, by the way.
You need to inspect the headers returned from the URL.
If you get HTTP return codes 301 or 302, then you are being notified that the page is redirecting you to another URL.
See http://www.w3.org/Protocols/HTTP/HTRESP.html for more details about HTTP return codes.