Lakritzator / harmony

C# library for connecting to and controlling the Logitech Harmony Hub.
MIT License
5 stars 0 forks source link

Change GetUserAuthToken #3

Closed Lakritzator closed 8 years ago

Lakritzator commented 8 years ago

I had a quick look at the "GetUserAuthToken" method implementation, which gets a token via a login at Logitech. This is written with HttpWebRequest, the implementation will cause issues when someone is behind a proxy. (This is why I wrote Dapplo.HttpExtensions, as most people implement HTTP calls wrong)

Just for fun, here is an implementation of the GetUserAuthToken with Dapplo.HttpExtensions:

< promotion mode on >

    /// <summary>
    /// Logs in to the Logitech Harmony web service to get a UserAuthToken.
    /// </summary>
    /// <param name="username">myharmony.com username</param>
    /// <param name="password">myharmony.com password</param>
    /// <returns>MyHarmony UserAuthToken</returns>
    private static async Task<string> GetUserAuthToken(string username, string password)
    {
        var myharmonyApiUri = new Uri("https://svcs.myharmony.com/CompositeSecurityServices/Security.svc/json");
        var getUserAuthTokenUri = myharmonyApiUri.AppendSegments("GetUserAuthToken");

        // Post (async) the email and password as Json
        // Expect a result of GetUserAuthTokenResultRootObject, but in case of error UserAuthErrorDetails
        var result = await getUserAuthTokenUri.PostAsync<HttpResponse<GetUserAuthTokenResultRootObject, UserAuthErrorDetails>>(new {
            email = username,
            password
        });

        // check if there was an error, throw an exception with the error Message from myharmony
        if (result.HasError) {
            throw new Exception(result.ErrorResponse?.Message);
        }

        // Everything was fine, return the answer
        return result.Response?.GetUserAuthTokenResult?.UserAuthToken;
    }

Here the original:

    /// <summary>
    /// Logs in to the Logitech Harmony web service to get a UserAuthToken.
    /// </summary>
    /// <param name="username">myharmony.com username</param>
    /// <param name="password">myharmony.com password</param>
    /// <returns>Logitech UserAuthToken</returns>
    private static string GetUserAuthToken(string username, string password)
    {
        const string logitechAuthUrl = "https://svcs.myharmony.com/CompositeSecurityServices/Security.svc/json/GetUserAuthToken";

        var httpWebRequest = (HttpWebRequest)WebRequest.Create(logitechAuthUrl);
        httpWebRequest.ContentType = "text/json";
        httpWebRequest.Method = "POST";

        using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
        {
            var json = new JavaScriptSerializer().Serialize(new
            {
                email = username,
                password
            });

            streamWriter.Write(json);
            streamWriter.Flush();
        }

        var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();

        var responseStream = httpResponse.GetResponseStream();
        if (responseStream == null)
        {
            return null;
        }

        string result;
        using (var streamReader = new StreamReader(responseStream))
        {
            result = streamReader.ReadToEnd();
        }
        var harmonyData = new JavaScriptSerializer().Deserialize<GetUserAuthTokenResultRootObject>(result);
        return harmonyData.GetUserAuthTokenResult.UserAuthToken;
    }

Advantage old vs new:

Disadvantage new vs old:

Advantage new vs old:

and most importantly it has error checking: it processes the Json response depending on an HTTP error differently! It either gives a normal response (as before, when no error) or an UserAuthErrorDetails when there was an error... packed in a wrapper, but you can write your own, where you can just check "if (result.HasError)" and e.g. create an exception which in case of a bad password passes the error message from logitech and looks like this:

Authentication failure BadCredentials : Unable to authenticate user's mail with the corresponding password via LIP:: 1234@domain.com </ promotion mode off >

Lakritzator commented 8 years ago

One way of going forward is having the "Json" classes available public (not internal) and also all the information like the Uri, so we make a simple implementation which handles most simple stuff and everything else can be handled in the calling application.

In that way, Dapplo or another client can be used.

BUT, I at least want to change from HttpWebRequest to HttpClient which is more modern/current.

Lakritzator commented 8 years ago

Done.