C# Works with Nest API Restful Auth using HttpClient - c#

Still being new to C#, I am sure that I am not using the HttpClient library properly. I am trying to authenticate with the Works With Nest API so that I can read/write requests to a thermostat. Below is the code that I am using to authenticate:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System.Net.Http;
using System.Net.Http.Headers;
namespace iConnect.Controllers
{
public class NestController : Controller
{
static HttpClient client = new HttpClient();
public IActionResult Index()
{
return View();
}
public async Task<HttpResponseMessage> GetNestAuthCode()
{
// Nest Pin Code
String pincode = "MYPING";
String clientID = "My-Client-ID";
String clientSecret = "MySecretString";
String grantType = "authorization_code";
client.BaseAddress = new Uri("https://api.home.nest.com");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var request = new HttpRequestMessage(HttpMethod.Post, "/oauth2/access_token");
var data = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("code", pincode)
, new KeyValuePair<string, string>("client_id", clientID)
, new KeyValuePair<string, string>("client_secret", clientSecret)
, new KeyValuePair<string, string>("grant_type", grantType)
};
//var content = new FormUrlEncodedContent(data);
//await content.ReadAsByteArrayAsync();
//content.Add(data);
request.Content = new FormUrlEncodedContent(data);
//HttpResponseMessage response = await client.PostAsync(client.BaseAddress, content);
HttpResponseMessage response = await client.SendAsync(request);
return response;
}
}
}
When I go to localhost:9387/Nest/GetAuthCode, I get the following JSON response:
{
"version":{
"major":1,
"minor":1,
"build":-1,
"revision":-1,
"majorRevision":-1,
"minorRevision":-1
},
"content":{
"headers":[
{
"key":"Content-Type",
"value":[
"application/json"
]
}
]
},
"statusCode":400,
"reasonPhrase":"Bad Request",
"headers":[
{
"key":"Connection",
"value":[
"keep-alive"
]
}
],
"requestMessage":{
"version":{
"major":1,
"minor":1,
"build":-1,
"revision":-1,
"majorRevision":-1,
"minorRevision":-1
},
"content":{
"headers":[
{
"key":"Content-Type",
"value":[
"application/x-www-form-urlencoded"
]
},
{
"key":"Content-Length",
"value":[
"130"
]
}
]
},
"method":{
"method":"POST"
},
"requestUri":"https://api.home.nest.com/oauth2/access_token",
"headers":[
{
"key":"Accept",
"value":[
"application/json"
]
}
],
"properties":{
}
},
"isSuccessStatusCode":false
}
Any assistance is greatly appreciated. Thank you.
EDIT:
I've made the following changes and get the following response (which is not what I expect):
Code:
public async Task<ActionResult> GetNestAuthCode()
{
// Nest Pin Code
String pincode = "MYPING";
String clientID = "My-Client-ID";
String clientSecret = "MySecretString";
String grantType = "authorization_code";
client.BaseAddress = new Uri("https://api.home.nest.com");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//var request = new HttpRequestMessage(HttpMethod.Post, "/oauth2/access_token");
var data = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("code", pincode)
, new KeyValuePair<string, string>("client_id", clientID)
, new KeyValuePair<string, string>("client_secret", clientSecret)
, new KeyValuePair<string, string>("grant_type", grantType)
};
//var content = new FormUrlEncodedContent(data);
//await content.ReadAsByteArrayAsync();
//content.Add(data);
//request.Content = new FormUrlEncodedContent(data);
//HttpResponseMessage response = await client.PostAsync(client.BaseAddress, content);
var response = await client.PostAsync("oauth2/access_token",
new FormUrlEncodedContent(data));
var content = await response.Content.ReadAsStringAsync();
return Content(content);
}
Response:
{"error":"oauth2_error","error_description":"authorization code not found","instance_id":"f64d5268-8bec-4799-927c-e53454ed96d5"}

You're returning you're whole response message back from your action method, with all its properties and values. Instead, you should just read its content and return that. You can find a good article on using the built in .NET HttpClient right here, if you'd like.
What I would do, is the following:
public async Task<IActionResult> GetNestAuthCode()
{
// HttpClient setup...
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("code", "MYPING"),
new KeyValuePair<string, string>("client_id", "My-Client-ID"),
new KeyValuePair<string, string>("client_secret", "MySecretString"),
new KeyValuePair<string, string>("grant_type", "authorization_code")
});
var response = await client.PostAsync("oauth2/access_token", content);
// Or check instead with IsSuccessStatusCode
response.EnsureSuccessStatusCode();
// ReadAsStringAsync() is just an example here
var responseContent = await response.Content.ReadAsStringAsync();
return Content(responseContent);
}
Note that...
I'm still using an IActionResult (or actually Task<IActionResult>) as return type. You shouldn't return the response object.
I'm directly using the PostAsync() method with the relevant content as FormUrlEncodedContent, instead of first building an HttpRequestMessage and using SendAsync().
Also don't forget to check if your request was successful! Otherwise, you might be returning an error message from your action method instead.
I'm just using ReadAsStringAsync() and return Content() as an example.
Please note that there is something wrong with your request to the Nest API though, since it returns 400 Bad Request. You should be able to derive what exactly from the error message in the content. ;)
EDIT
I had a very quick look at the Nest API, and I think you're giving the wrong value for code. Instead of specifying your PIN code, I think you should first call another API method to retrieve an authorization code before you can exchange it for an access token (as seen here).

Related

Not Response get access token oauth 2.0 API UBER in ASP NET C#

I'm using this method, I recover the error "The interruption of an existing connection has been forced by the remote host"
public static async void GetAccessToken()
{
var client = new HttpClient();
// Create the HttpContent for the form to be posted.
var requestContent = new FormUrlEncodedContent(
new[] {
new KeyValuePair<string, string>("client_secret","xxxxx"),
new KeyValuePair<string, string>("client_id","xxxxx"),
new KeyValuePair<string, string>("grant_type","authorization_code"),
new KeyValuePair<string, string>("redirect_uri","http://localhost"),
new KeyValuePair<string, string>("code","xxxxx")
});
// Get the response.
HttpResponseMessage response = await client.PostAsync(
"https://domain-uber.com/oauth/v2/token",
requestContent);
// Get the response content.
HttpContent responseContent = response.Content;
// Get the stream of the content.
using (var reader = new StreamReader(await responseContent.ReadAsStreamAsync()))
{
var result = await reader.ReadToEndAsync();
}
}

Supplying api key in HttpClient's headers not working

I'm trying to get data from https://ndb.nal.usda.gov, basing on instructions provided here https://ndb.nal.usda.gov/ndb/doc/apilist/API-FOOD-REPORT.md
I'm using HttpClient as follows, but can't seem to be able to pass the api_key correctly. Shouldn't I be adding it to headers? I tried
public static async Task<string> DownloadItemData(string itemName)
{
using (HttpClient client = new HttpClient())
{
HttpContent requestContent =
new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("api_key", "myKey"),
new KeyValuePair<string, string>("ndbno", "01009"),
new KeyValuePair<string, string>("type", "f")
});
requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await client.PostAsync("http://api.nal.usda.gov/ndb/reports", requestContent);
using (StreamReader reader = new StreamReader(await response.Content.ReadAsStreamAsync()))
{
// Write the output.
string result = await reader.ReadToEndAsync();
return result;
}
}
But all I'm getting is:
{
"error": {
"code": "API_KEY_MISSING",
"message": "No api_key was supplied. Get one at http://api.nal.usda.gov"
}
}
I also tried this, but with the same result:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("api_key", "myKey");
Any ideas how to pass the api_key correctly? Thanks in advance!

Google Play Music Api Unauthorized Response

First up I am aware that the api is not supported.
https://stackoverflow.com/questions/16707164/is-there-a-google-play-music-api
I have been attempting to reverse engineer some of the apis in the above post. These have been the only real source of information as no where else documents it.
I have successfully been able to implement a oauth token retrieval system.
However when I pass my token To the following I get Forbidden Error 403
Url =
https://play.google.com/music/services/streamingloadalltracks?format=jsarray
Headers =
client.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse(String.Format("GoogleLogin auth={0}", _token));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", _token);
Code
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(paramObj.Base);
client.Timeout = TimeSpan.FromSeconds(5);
if (!paramObj.skipAuth)
{
client.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse(String.Format("GoogleLogin auth={0}", _token));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", _token);
}
//client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
//client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
//client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("sdch"));
try
{
if (paramObj.post)
{
return client
.PostAsync(paramObj.method + paramObj.queryString, paramObj.content)
.Result
.Content
.ReadAsStringAsync()
//.ReadAsByteArrayAsync()
.Result;
}
else
{
return client
.GetAsync(paramObj.method + paramObj.queryString)
.Result
.Content
.ReadAsStringAsync()
//.ReadAsByteArrayAsync()
.Result;
}
}
catch (Exception exception)
{
return null;
}
}
Question is why isnt this working. I have been looking into the other apis and they implement the same calls(i think my python is pretty rusty)
Token Generator.
public string Oauth2AuthorizeUrl =>
string.Format(
"{0}?response_type=code&client_id={1}&redirect_uri={2}&scope={3}&access_type=offline",
GooglePlayAuthorizeMethod,
ClientId,
HttpUtility.UrlEncode(GooglePlayAuthorizeRedirectURL),
GooglePlayAuthorizeScope
);
public void Oauth2RetrieveToken(string code)
{
var querystring = string.Format(
"?response_type=code&code={0}client_id={1}&client_secret={3}&redirect_uri={2}&grant_type=authorization_code",
code,
ClientId,
HttpUtility.UrlEncode(GooglePlayAuthorizeRedirectURL),
ClientSecret
);
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://accounts.google.com");
client.Timeout = TimeSpan.FromSeconds(5);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("client_id", ClientId),
new KeyValuePair<string, string>("client_secret", ClientSecret),
new KeyValuePair<string, string>("redirect_uri", GooglePlayAuthorizeRedirectURL),
new KeyValuePair<string, string>("grant_type", "authorization_code")
});
var result = client
.PostAsync(GooglePlayRequestToken , content)
.Result
.Content
.ReadAsStringAsync()
.Result;
var value = JObject.Parse(result);
var libs = User.Libs;
libs.GooglePlayPassword = value["access_token"].ToString();
User.UpdateSettings(libs);
}
}
Check out my attempt of an google play music API here (https://github.com/coman3/Google.Music)).
It's working perfectly when it comes to authentication and calls (although does not support 2 factor auth).

Send request as Json on UWP

I have deployed an AzureML published experiment with deployed web service. I tried to use the sample code provided in the configuration page, but universal apps do not implement Http.Formatting yet, thus I couldn't use postasjsonasync.
I tried to follow the sample code as much as possible, but I'm getting statuscode of 415 "Unsupported Media Type", What's the mistake I'm doing?
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
// client.BaseAddress = uri;
var scoreRequest = new
{
Inputs = new Dictionary<string, StringTable>() {
{
"dataInput",
new StringTable()
{
ColumnNames = new [] {"Direction", "meanX", "meanY", "meanZ"},
Values = new [,] { { "", x.ToString(), y.ToString(), z.ToString() }, }
}
},
},
GlobalParameters = new Dictionary<string, string>() { }
};
var stringContent = new StringContent(scoreRequest.ToString());
HttpResponseMessage response = await client.PostAsync(uri, stringContent);
Many Thanks
You'll need to serialize the object to a JSON string (I recommend using NewtonSoft.Json to make it easier) and set the content type accordingly. Here's an implementation I'm using in my UWP apps (note that _client is an HttpClient):
public async Task<HttpResponseMessage> PostAsJsonAsync<T>(Uri uri, T item)
{
var itemAsJson = JsonConvert.SerializeObject(item);
var content = new StringContent(itemAsJson);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return await _client.PostAsync(uri, content);
}

Using HttpClient in Xamarin PCL to post

I'm trying to make a post to a rest api using HttpClient PCL in Xamarin. I have the following code but I'm getting a 404. Note that I don't have the real ip address here but when I use Hurl.It with the real one it connects just fine, but here just a 404. What am I missing?
string url = "http://myaddresshere/services/rest/auth/login";
string result = String.Empty;
using (var client = new HttpClient()) {
var content = new FormUrlEncodedContent(new[] {
new KeyValuePair<string, string>(txtUsername.Text, "username"),
new KeyValuePair<string, string>(txtPassword.Text, "password")
});
using (var response = await client.PostAsync(url, content)) {
using (var responseContent = response.Content) {
result = await responseContent.ReadAsStringAsync();
}
}
}
You should probably switch in your code:
new KeyValuePair<string, string>(txtUsername.Text, "username"),
new KeyValuePair<string, string>(txtPassword.Text, "password")
To:
new KeyValuePair<string, string>("username", txtUsername.Text),
new KeyValuePair<string, string>("password", txtPassword.Text)

Categories