Ronny
Ronny

Reputation: 157

C# Works with Nest API Restful Auth using HttpClient

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"}

Upvotes: 1

Views: 1177

Answers (1)

DeveTho
DeveTho

Reputation: 391

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).

Upvotes: 1

Related Questions